perf(allocations): reduced overall allocations by 95%, increaseed jacobian applicatin by 2x
This commit uses global pre allocated work space to dramatically reduce memory usage and allocation time
This commit is contained in:
159
libmeanfield/impl/fem/reference_tables.cpp
Normal file
159
libmeanfield/impl/fem/reference_tables.cpp
Normal file
@@ -0,0 +1,159 @@
|
||||
module;
|
||||
|
||||
#include <array>
|
||||
#include <cmath>
|
||||
#include <functional>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <stdexcept>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include <mfem.hpp>
|
||||
|
||||
module mean_field;
|
||||
import :fem.reference_tables;
|
||||
|
||||
namespace mean_field::fem {
|
||||
namespace {
|
||||
struct ReferenceTableKey {
|
||||
const mfem::FiniteElement *element;
|
||||
std::vector<std::array<double, 4>> points;
|
||||
|
||||
bool operator<(const ReferenceTableKey &other) const {
|
||||
if (element != other.element)
|
||||
return std::less<const mfem::FiniteElement *>{}(element, other.element);
|
||||
return points < other.points;
|
||||
}
|
||||
};
|
||||
|
||||
ReferenceTableKey make_key(
|
||||
const mfem::FiniteElement &element,
|
||||
const mfem::IntegrationRule &rule
|
||||
) {
|
||||
ReferenceTableKey key{.element = &element, .points = {}};
|
||||
key.points.reserve(rule.GetNPoints());
|
||||
for (int q = 0; q < rule.GetNPoints(); ++q) {
|
||||
const auto &point = rule.IntPoint(q);
|
||||
const std::array<double, 4> values{
|
||||
point.x, element.GetDim() > 1 ? point.y : 0.0, element.GetDim() > 2 ? point.z : 0.0, point.weight
|
||||
};
|
||||
for (const double value : values) {
|
||||
if (!std::isfinite(value))
|
||||
throw std::invalid_argument("Reference table quadrature entries must be finite.");
|
||||
}
|
||||
key.points.push_back(values);
|
||||
}
|
||||
return key;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
struct ReferenceTableCache::Storage {
|
||||
std::mutex mutex;
|
||||
std::map<ReferenceTableKey, std::shared_ptr<const ScalarReferenceTable>> scalar_tables;
|
||||
std::map<ReferenceTableKey, std::shared_ptr<const VectorReferenceTable>> vector_tables;
|
||||
};
|
||||
|
||||
ReferenceTableCache::ReferenceTableCache() : m_storage(std::make_unique<Storage>()) {
|
||||
}
|
||||
ReferenceTableCache::~ReferenceTableCache() = default;
|
||||
|
||||
std::shared_ptr<const ScalarReferenceTable> ReferenceTableCache::GetScalarTable(
|
||||
const mfem::FiniteElement &element,
|
||||
const mfem::IntegrationRule &rule
|
||||
) const {
|
||||
if (element.GetRangeType() != mfem::FiniteElement::SCALAR)
|
||||
throw std::invalid_argument("A scalar reference table requires a scalar finite element.");
|
||||
auto key = make_key(element, rule);
|
||||
const std::lock_guard lock(m_storage->mutex);
|
||||
if (const auto found = m_storage->scalar_tables.find(key); found != m_storage->scalar_tables.end())
|
||||
return found->second;
|
||||
auto table = std::shared_ptr<const ScalarReferenceTable>(new ScalarReferenceTable(element, rule));
|
||||
m_storage->scalar_tables.emplace(std::move(key), table);
|
||||
return table;
|
||||
}
|
||||
|
||||
std::shared_ptr<const VectorReferenceTable> ReferenceTableCache::GetVectorTable(
|
||||
const mfem::FiniteElement &element,
|
||||
const mfem::IntegrationRule &rule
|
||||
) const {
|
||||
if (element.GetRangeType() != mfem::FiniteElement::VECTOR)
|
||||
throw std::invalid_argument("A vector reference table requires a vector finite element.");
|
||||
auto key = make_key(element, rule);
|
||||
const std::lock_guard lock(m_storage->mutex);
|
||||
if (const auto found = m_storage->vector_tables.find(key); found != m_storage->vector_tables.end())
|
||||
return found->second;
|
||||
auto table = std::shared_ptr<const VectorReferenceTable>(new VectorReferenceTable(element, rule));
|
||||
m_storage->vector_tables.emplace(std::move(key), table);
|
||||
return table;
|
||||
}
|
||||
|
||||
ScalarReferenceTable::ScalarReferenceTable(
|
||||
const mfem::FiniteElement &element,
|
||||
const mfem::IntegrationRule &rule
|
||||
)
|
||||
: m_values(
|
||||
rule.GetNPoints(),
|
||||
element.GetDof()
|
||||
),
|
||||
m_dimension(element.GetDim()) {
|
||||
mfem::Vector values(element.GetDof());
|
||||
if (element.GetDerivType() == mfem::FiniteElement::GRAD)
|
||||
m_gradients.resize(rule.GetNPoints());
|
||||
for (int q = 0; q < rule.GetNPoints(); ++q) {
|
||||
const auto &point = rule.IntPoint(q);
|
||||
element.CalcShape(point, values);
|
||||
for (int dof = 0; dof < element.GetDof(); ++dof)
|
||||
m_values(q, dof) = values(dof);
|
||||
if (!m_gradients.empty()) {
|
||||
auto &gradient = m_gradients[q];
|
||||
gradient.SetSize(element.GetDof(), m_dimension);
|
||||
element.CalcDShape(point, gradient);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const mfem::DenseMatrix &ScalarReferenceTable::GetValues() const {
|
||||
return m_values;
|
||||
}
|
||||
const mfem::DenseMatrix &ScalarReferenceTable::GetGradients(const int point) const {
|
||||
return m_gradients.at(point);
|
||||
}
|
||||
int ScalarReferenceTable::GetPointCount() const {
|
||||
return m_values.Height();
|
||||
}
|
||||
int ScalarReferenceTable::GetDofCount() const {
|
||||
return m_values.Width();
|
||||
}
|
||||
int ScalarReferenceTable::GetDimension() const {
|
||||
return m_dimension;
|
||||
}
|
||||
|
||||
VectorReferenceTable::VectorReferenceTable(
|
||||
const mfem::FiniteElement &element,
|
||||
const mfem::IntegrationRule &rule
|
||||
)
|
||||
: m_dof_count(element.GetDof()),
|
||||
m_dimension(element.GetRangeDim()) {
|
||||
m_values.resize(rule.GetNPoints());
|
||||
for (int q = 0; q < rule.GetNPoints(); ++q) {
|
||||
auto &values = m_values[q];
|
||||
values.SetSize(m_dof_count, m_dimension);
|
||||
element.CalcVShape(rule.IntPoint(q), values);
|
||||
}
|
||||
}
|
||||
|
||||
const mfem::DenseMatrix &VectorReferenceTable::GetValues(const int point) const {
|
||||
return m_values.at(point);
|
||||
}
|
||||
int VectorReferenceTable::GetPointCount() const {
|
||||
return static_cast<int>(m_values.size());
|
||||
}
|
||||
int VectorReferenceTable::GetDofCount() const {
|
||||
return m_dof_count;
|
||||
}
|
||||
int VectorReferenceTable::GetDimension() const {
|
||||
return m_dimension;
|
||||
}
|
||||
} // namespace mean_field::fem
|
||||
Reference in New Issue
Block a user