Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions cpp/dolfinx/common/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ set(HEADERS_common
${CMAKE_CURRENT_SOURCE_DIR}/sort.h
${CMAKE_CURRENT_SOURCE_DIR}/types.h
${CMAKE_CURRENT_SOURCE_DIR}/math.h
${CMAKE_CURRENT_SOURCE_DIR}/memory.h
${CMAKE_CURRENT_SOURCE_DIR}/memory_fwd.h
${CMAKE_CURRENT_SOURCE_DIR}/MPI.h
${CMAKE_CURRENT_SOURCE_DIR}/Scatterer.h
${CMAKE_CURRENT_SOURCE_DIR}/Table.h
Expand Down
17 changes: 16 additions & 1 deletion cpp/dolfinx/common/IndexMap.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// Copyright (C) 2015-2024 Chris Richardson, Garth N. Wells, Igor Baratta,
// Joseph P. Dean and Jørgen S. Dokken
// Joseph P. Dean, Jørgen S. Dokken and Paul T. Kühner
//
// This file is part of DOLFINx (https://www.fenicsproject.org)
//
Expand Down Expand Up @@ -1374,3 +1374,18 @@ std::array<std::vector<int>, 2> IndexMap::rank_type(int split_type) const
return {std::move(split_dest), std::move(split_src)};
}
//-----------------------------------------------------------------------------

std::size_t dolfinx::common::impl::memory(const IndexMap& im)
{
std::size_t size = 0;

size += sizeof(IndexMap);
size += memory(im.local_range());
size += memory(im.ghosts());
size += memory(im.owners());
size += memory(im.src());
size += memory(im.dest());

return size;
}
//-----------------------------------------------------------------------------
7 changes: 6 additions & 1 deletion cpp/dolfinx/common/IndexMap.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@

#include "IndexMap.h"
#include "MPI.h"
#include "memory.h"
#include <cstdint>
#include <dolfinx/graph/AdjacencyList.h>
#include <memory>
#include <span>
#include <tuple>
#include <utility>
Expand Down Expand Up @@ -332,4 +332,9 @@ class IndexMap
std::vector<int> _dest;
};

namespace impl
{
std::size_t memory(const IndexMap& im);
}

} // namespace dolfinx::common
107 changes: 107 additions & 0 deletions cpp/dolfinx/common/memory.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
// Copyright (C) 2025 Paul T. Kühner
//
// This file is part of DOLFINx (https://www.fenicsproject.org)
//
// SPDX-License-Identifier: LGPL-3.0-or-later

#pragma once

#include <algorithm>
#include <cstddef>
#include <cstdint>
#include <memory>
#include <span>
#include <type_traits>
#include <vector>

#include <basix/mdspan.hpp>

#include "memory_fwd.h"
#include "types.h"

namespace dolfinx::common
{

namespace impl
{

template <typename T>
std::size_t memory(const T& /* obj */)
{
static_assert(false, "Memory usage not supported for provided type.");
return 0;
}

template <typename S, std::size_t N>
requires std::is_arithmetic_v<S>
std::size_t memory(const std::array<S, N>& array)
{
return sizeof(array);
}

template <typename S>
requires std::is_arithmetic_v<S>
std::size_t memory(const std::vector<S>& vec)
{
using value_type = typename std::vector<S>::value_type;

std::size_t size_type = sizeof(vec);
std::size_t size_data = vec.capacity() * sizeof(value_type);
return size_type + size_data;
}

// TODO: document ownership assumption
template <typename S>
requires std::is_arithmetic_v<S>
std::size_t memory(const std::span<const S>& span)
{
using value_type = typename std::vector<S>::value_type;

std::size_t size_type = sizeof(span);
std::size_t size_data = span.size() * sizeof(value_type);
return size_type + size_data;
}

template <typename T>
std::size_t memory(const std::vector<std::vector<T>>& vec)
{
std::size_t size = sizeof(vec);
std::ranges::for_each(vec, [&](const auto& e) { size += memory(e); });
return size;
}

template <typename S, class Extents, class LayoutPolicy = md::layout_right,
class AccessorPolicy = md::default_accessor<S>>
requires std::is_arithmetic_v<S>
std::size_t
memory(const md::mdspan<S, Extents, LayoutPolicy, AccessorPolicy>& mdspan)
{
using value_type = typename std::mdspan<S, Extents, LayoutPolicy,
AccessorPolicy>::value_type;

// TODO: object size? - what happens for compile time sized mdspans?
std::size_t size_data = mdspan.size() * sizeof(value_type);

return size_data;
}

} // namespace impl

constexpr std::integral_constant<std::int64_t, 1> byte;
constexpr std::integral_constant<std::int64_t, 1'024> kilobyte;
constexpr std::integral_constant<std::int64_t, 1'048'576> megabyte;
constexpr std::integral_constant<std::int64_t, 1'073'741'824> gigabyte;
constexpr std::integral_constant<std::int64_t, 1'099'511'627'776> terabyte;

template <typename T, std::int64_t U = 1>
std::conditional_t<U == 1, std::size_t, double>
memory(const T& obj, std::integral_constant<std::int64_t, U> bytes_per_unit)
{
std::size_t bytes = impl::memory(obj);
if constexpr (bytes_per_unit == byte)
return bytes;
else
return static_cast<double>(bytes) / bytes_per_unit.value;
}

} // namespace dolfinx::common
33 changes: 33 additions & 0 deletions cpp/dolfinx/common/memory_fwd.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// Copyright (C) 2025 Paul T. Kühner
//
// This file is part of DOLFINx (https://www.fenicsproject.org)
//
// SPDX-License-Identifier: LGPL-3.0-or-later

#pragma once

#include <concepts>

namespace dolfinx
{

namespace common
{
class IndexMap;
}

namespace mesh
{
template <std::floating_point T>
class Geometry;
}

namespace common::impl
{
std::size_t memory(const IndexMap& im);

template <std::floating_point T>
std::size_t memory(const dolfinx::mesh::Geometry<T>& geometry);
} // namespace common::impl

} // namespace dolfinx
28 changes: 27 additions & 1 deletion cpp/dolfinx/mesh/Geometry.h
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright (C) 2006-2022 Anders Logg and Garth N. Wells
// Copyright (C) 2006-2022 Anders Logg, Garth N. Wells and Paul T. Kühner
//
// This file is part of DOLFINx (https://www.fenicsproject.org)
//
Expand Down Expand Up @@ -328,3 +328,29 @@ create_geometry(const Topology& topology,
}

} // namespace dolfinx::mesh

namespace dolfinx::common::impl
{

template <std::floating_point T>
std::size_t memory(const dolfinx::mesh::Geometry<T>& geometry)
{

std::size_t size = 0;
size += sizeof(geometry);

size += memory(*geometry.index_map());

for (std::size_t i = 0; i < geometry.cmaps().size(); i++)
{
size += memory(geometry.dofmap(i));
// TODO: requires heavy work for basix::finite_element
// size += memory(geometry.cmaps()[i]);
}

size += memory(geometry.x());
size += memory(geometry.input_global_indices());

return size;
};
} // namespace dolfinx::common::impl
1 change: 1 addition & 0 deletions cpp/test/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ add_executable(
common/CIFailure.cpp
common/sub_systems_manager.cpp
common/index_map.cpp
common/memory.cpp
common/sort.cpp
fem/form.cpp
fem/functionspace.cpp
Expand Down
126 changes: 126 additions & 0 deletions cpp/test/common/memory.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
// Copyright (C) 2025 Paul T. Kühner
//
// This file is part of DOLFINx (https://www.fenicsproject.org)
//
// SPDX-License-Identifier: LGPL-3.0-or-later

#include <catch2/catch_approx.hpp>
#include <catch2/catch_template_test_macros.hpp>
#include <catch2/catch_test_macros.hpp>

#include <mpi.h>
#include <vector>

#include "dolfinx/common/memory.h"
#include "dolfinx/mesh/Geometry.h"
#include "dolfinx/mesh/generation.h"

using namespace dolfinx::common;

TEMPLATE_TEST_CASE("memory-array", "[memory]", std::int16_t, std::int32_t,
std::int64_t, std::uint16_t, std::uint32_t, std::uint64_t,
float, double)
// std::complex<float>, std::complex<double>
{
std::array<TestType, 10> v;

std::size_t bytes = 10 * sizeof(TestType);

CHECK(memory(v, byte) == bytes);

CHECK(memory(v, kilobyte)
== Catch::Approx(static_cast<double>(bytes) / kilobyte));
CHECK(memory(v, megabyte)
== Catch::Approx(static_cast<double>(bytes) / megabyte));
CHECK(memory(v, gigabyte)
== Catch::Approx(static_cast<double>(bytes) / gigabyte));
CHECK(memory(v, terabyte)
== Catch::Approx(static_cast<double>(bytes) / terabyte));
}

TEMPLATE_TEST_CASE("memory-vector", "[memory]", std::int16_t, std::int32_t,
std::int64_t, std::uint16_t, std::uint32_t, std::uint64_t,
float, double)
// std::complex<float>, std::complex<double>
{
std::vector<TestType> v;
v.reserve(10);

std::size_t bytes = sizeof(std::vector<TestType>) + 10 * sizeof(TestType);

CHECK(memory(v, byte) == bytes);

CHECK(memory(v, kilobyte)
== Catch::Approx(static_cast<double>(bytes) / kilobyte));
CHECK(memory(v, megabyte)
== Catch::Approx(static_cast<double>(bytes) / megabyte));
CHECK(memory(v, gigabyte)
== Catch::Approx(static_cast<double>(bytes) / gigabyte));
CHECK(memory(v, terabyte)
== Catch::Approx(static_cast<double>(bytes) / terabyte));
}

TEMPLATE_TEST_CASE("memory-span", "[memory]", std::int16_t, std::int32_t,
std::int64_t, std::uint16_t, std::uint32_t, std::uint64_t,
float, double)
// std::complex<float>, std::complex<double>
{
std::array<TestType, 10> v;
std::span span{v};

std::size_t bytes = 10 * sizeof(TestType);

CHECK(memory(v, byte) == bytes);

CHECK(memory(v, kilobyte)
== Catch::Approx(static_cast<double>(bytes) / kilobyte));
CHECK(memory(v, megabyte)
== Catch::Approx(static_cast<double>(bytes) / megabyte));
CHECK(memory(v, gigabyte)
== Catch::Approx(static_cast<double>(bytes) / gigabyte));
CHECK(memory(v, terabyte)
== Catch::Approx(static_cast<double>(bytes) / terabyte));
}

TEMPLATE_TEST_CASE("memory-vector-vector", "[memory]", std::int16_t,
std::int32_t, std::int64_t, std::uint16_t, std::uint32_t,
std::uint64_t, float, double)
// std::complex<float>, std::complex<double>
{
std::vector<std::vector<TestType>> v;
v.reserve(3);
v.template emplace_back<std::vector<TestType>>({{0, 1, 2}});
v.template emplace_back<std::vector<TestType>>({{0, 1, 2, 3}});
v.template emplace_back<std::vector<TestType>>({{0, 1, 2, 3, 4}});

std::size_t bytes = sizeof(std::vector<std::vector<TestType>>)
+ 3 * sizeof(std::vector<TestType>)
+ (3 + 4 + 5) * sizeof(TestType);

CHECK(memory(v, byte) == bytes);

CHECK(memory(v, kilobyte)
== Catch::Approx(static_cast<double>(bytes) / kilobyte));
CHECK(memory(v, megabyte)
== Catch::Approx(static_cast<double>(bytes) / megabyte));
CHECK(memory(v, gigabyte)
== Catch::Approx(static_cast<double>(bytes) / gigabyte));
CHECK(memory(v, terabyte)
== Catch::Approx(static_cast<double>(bytes) / terabyte));
}

TEST_CASE("memory-indexmap", "[memory]")
{
auto im = IndexMap(MPI_COMM_WORLD, 10);
CHECK(memory(im, byte) > 0);
}

TEMPLATE_TEST_CASE("memory-geometry", "[memory]", float, double)
{
auto mesh = dolfinx::mesh::create_rectangle<TestType>(
MPI_COMM_SELF, {{{0, 0}, {1, 1}}}, {1, 1},
dolfinx::mesh::CellType::quadrilateral);

const auto& geo = mesh.geometry();
CHECK(memory<dolfinx::mesh::Geometry<TestType>>(geo, byte) > 0);
}
Loading
Loading