feat(preconditioner): major work on preconditioner system

first preconditioner MVP
This commit is contained in:
2026-09-04 07:54:10 -04:00
parent 25510008dd
commit 71423d543f
61 changed files with 15920 additions and 422 deletions

View File

@@ -1,194 +1,216 @@
#pragma once
#include <algorithm>
#include <chrono>
#include <cmath>
#include <iomanip>
#include <iostream>
#include <limits>
#include <cstddef>
#include <cstdint>
#include <iosfwd>
#include <map>
#include <mutex>
#include <memory>
#include <string>
#include <string_view>
#include <vector>
#include <mpi.h>
#ifndef MEAN_FIELD_ENABLE_PROFILING
#define MEAN_FIELD_ENABLE_PROFILING 0
#endif
namespace mean_field::profiling {
struct Statistics {
unsigned long long observations{0};
unsigned long long warmups{0};
unsigned long long samples{0};
unsigned long long warmup_target{0};
std::uint64_t observations{0};
std::uint64_t warmups{0};
std::uint64_t samples{0};
std::uint64_t warmup_target{0};
std::uint64_t work_units{0};
double total_seconds{0.0};
double minimum_seconds{std::numeric_limits<double>::infinity()};
double minimum_seconds{0.0};
double maximum_seconds{0.0};
};
struct DistributedStatistics {
std::string label;
std::uint64_t minimum_samples{0};
std::uint64_t maximum_samples{0};
std::uint64_t maximum_warmups{0};
std::uint64_t minimum_work_units{0};
std::uint64_t maximum_work_units{0};
double maximum_rank_average_seconds{0.0};
double global_minimum_seconds{0.0};
double global_maximum_seconds{0.0};
double maximum_rank_total_seconds{0.0};
};
class Registry {
public:
static Registry& Get() {
static Registry registry;
return registry;
}
static Registry &Get();
void Record(const std::string& label, const double seconds, const unsigned long long warmup_count) {
std::scoped_lock lock(m_mutex);
Statistics& statistics = m_statistics[label];
Registry(const Registry &) = delete;
Registry &operator=(const Registry &) = delete;
Registry(Registry &&) = delete;
Registry &operator=(Registry &&) = delete;
statistics.warmup_target = std::max(statistics.warmup_target, warmup_count);
const bool is_warmup = statistics.observations < statistics.warmup_target;
++statistics.observations;
~Registry();
if (is_warmup) {
++statistics.warmups;
return;
}
void Record(
std::string_view label,
double seconds,
std::uint64_t warmup_count = 0
);
++statistics.samples;
statistics.total_seconds += seconds;
statistics.minimum_seconds = std::min(statistics.minimum_seconds, seconds);
statistics.maximum_seconds = std::max(statistics.maximum_seconds, seconds);
}
void AddCount(
std::string_view label,
std::uint64_t work_units
);
void Reset() {
std::scoped_lock lock(m_mutex);
m_statistics.clear();
}
void Reset();
void Print(MPI_Comm communicator) const {
const std::map<std::string, Statistics> snapshot = GetSnapshot();
[[nodiscard]] std::map<
std::string,
Statistics,
std::less<>>
Snapshot() const;
int mpi_initialized = 0;
int mpi_finalized = 0;
MPI_Initialized(&mpi_initialized);
if (mpi_initialized) MPI_Finalized(&mpi_finalized);
[[nodiscard]] std::vector<DistributedStatistics> Aggregate(MPI_Comm communicator) const;
const bool use_mpi = mpi_initialized && !mpi_finalized;
int rank = 0;
int communicator_size = 1;
void Print(
MPI_Comm communicator,
std::ostream &stream
) const;
if (use_mpi) {
MPI_Comm_rank(communicator, &rank);
MPI_Comm_size(communicator, &communicator_size);
}
void Print(MPI_Comm communicator) const;
if (rank == 0) {
std::cout << '\n';
std::cout << std::left << std::setw(42) << "Profile Region"
<< std::right << std::setw(11) << "Samples"
<< std::setw(10) << "Warmups"
<< std::setw(14) << "Avg Max ms"
<< std::setw(14) << "Min ms"
<< std::setw(14) << "Max ms"
<< std::setw(14) << "Total Max s" << '\n';
std::cout << std::string(119, '-') << '\n';
}
for (const auto& [label, local_statistics] : snapshot) {
unsigned long long minimum_samples = local_statistics.samples;
unsigned long long maximum_samples = local_statistics.samples;
unsigned long long maximum_warmups = local_statistics.warmups;
double local_average = local_statistics.samples > 0 ? local_statistics.total_seconds / static_cast<double>(local_statistics.samples) : 0.0;
double local_minimum = local_statistics.samples > 0 ? local_statistics.minimum_seconds : std::numeric_limits<double>::infinity();
double local_maximum = local_statistics.maximum_seconds;
double local_total = local_statistics.total_seconds;
double maximum_rank_average = local_average;
double global_minimum = local_minimum;
double global_maximum = local_maximum;
double maximum_rank_total = local_total;
if (use_mpi) {
MPI_Allreduce(&local_statistics.samples, &minimum_samples, 1, MPI_UNSIGNED_LONG_LONG, MPI_MIN, communicator);
MPI_Allreduce(&local_statistics.samples, &maximum_samples, 1, MPI_UNSIGNED_LONG_LONG, MPI_MAX, communicator);
MPI_Allreduce(&local_statistics.warmups, &maximum_warmups, 1, MPI_UNSIGNED_LONG_LONG, MPI_MAX, communicator);
MPI_Allreduce(&local_average, &maximum_rank_average, 1, MPI_DOUBLE, MPI_MAX, communicator);
MPI_Allreduce(&local_minimum, &global_minimum, 1, MPI_DOUBLE, MPI_MIN, communicator);
MPI_Allreduce(&local_maximum, &global_maximum, 1, MPI_DOUBLE, MPI_MAX, communicator);
MPI_Allreduce(&local_total, &maximum_rank_total, 1, MPI_DOUBLE, MPI_MAX, communicator);
}
if (!std::isfinite(global_minimum)) global_minimum = 0.0;
if (rank == 0) {
const std::string sample_string = minimum_samples == maximum_samples
? std::to_string(minimum_samples)
: std::to_string(minimum_samples) + "-" + std::to_string(maximum_samples);
std::cout << std::left << std::setw(100) << label
<< std::right << std::setw(11) << sample_string
<< std::setw(10) << maximum_warmups
<< std::setw(14) << std::fixed << std::setprecision(3) << 1.0e3 * maximum_rank_average
<< std::setw(14) << 1.0e3 * global_minimum
<< std::setw(14) << 1.0e3 * global_maximum
<< std::setw(14) << std::setprecision(6) << maximum_rank_total << '\n';
}
}
if (rank == 0) {
std::cout << std::string(119, '=') << '\n';
std::cout << "MPI ranks: " << communicator_size << "\n\n";
}
}
void PrintCsv(
MPI_Comm communicator,
std::ostream &stream
) const;
private:
[[nodiscard]] std::map<std::string, Statistics> GetSnapshot() const {
std::scoped_lock lock(m_mutex);
return m_statistics;
}
friend class Region;
Registry();
[[nodiscard]] std::size_t Register(
std::string_view label,
std::uint64_t warmup_count
);
void Record(
std::size_t region,
double seconds
) noexcept;
void AddCount(
std::size_t region,
std::uint64_t work_units
) noexcept;
struct Impl;
std::unique_ptr<Impl> m_impl;
};
class Region {
public:
explicit Region(
std::string_view label,
std::uint64_t warmup_count = 0
);
void Record(double seconds) const noexcept;
void AddCount(std::uint64_t work_units) const noexcept;
private:
mutable std::mutex m_mutex;
std::map<std::string, Statistics> m_statistics;
std::size_t m_region;
};
class ScopedTimer {
public:
ScopedTimer(std::string label, const unsigned long long warmup_count)
: m_label(std::move(label)),
m_warmup_count(warmup_count),
m_start(std::chrono::steady_clock::now()) {}
explicit ScopedTimer(const Region &region) noexcept;
ScopedTimer(const ScopedTimer&) = delete;
ScopedTimer& operator=(const ScopedTimer&) = delete;
ScopedTimer(ScopedTimer&&) = delete;
ScopedTimer& operator=(ScopedTimer&&) = delete;
ScopedTimer(const ScopedTimer &) = delete;
ScopedTimer &operator=(const ScopedTimer &) = delete;
ScopedTimer(ScopedTimer &&) = delete;
ScopedTimer &operator=(ScopedTimer &&) = delete;
~ScopedTimer() {
try {
const auto stop = std::chrono::steady_clock::now();
const double seconds = std::chrono::duration<double>(stop - m_start).count();
Registry::Get().Record(m_label, seconds, m_warmup_count);
} catch (...) {}
}
~ScopedTimer() noexcept;
private:
std::string m_label;
unsigned long long m_warmup_count;
const Region &m_region;
std::chrono::steady_clock::time_point m_start;
};
}
} // namespace mean_field::profiling
#define MEAN_FIELD_PROFILE_JOIN_IMPL(left, right) left##right
#define MEAN_FIELD_PROFILE_JOIN(left, right) MEAN_FIELD_PROFILE_JOIN_IMPL(left, right)
#define MEAN_FIELD_PROFILE_SCOPE_WARMUP(label, warmup_count) \
::mean_field::profiling::ScopedTimer MEAN_FIELD_PROFILE_JOIN(mean_field_profile_timer_, __COUNTER__)(label, warmup_count)
#if MEAN_FIELD_ENABLE_PROFILING
#define MEAN_FIELD_PROFILE_SCOPE(label) \
MEAN_FIELD_PROFILE_SCOPE_WARMUP(label, 1)
#define MEAN_FIELD_PROFILE_SCOPE_IMPL(label, warmup_count, identifier) \
static const ::mean_field::profiling::Region MEAN_FIELD_PROFILE_JOIN(mean_field_profile_region_, identifier)( \
label, warmup_count \
); \
const ::mean_field::profiling::ScopedTimer MEAN_FIELD_PROFILE_JOIN(mean_field_profile_timer_, identifier)( \
MEAN_FIELD_PROFILE_JOIN(mean_field_profile_region_, identifier) \
)
#define MEAN_FIELD_PROFILE_CALL_WARMUP(label, warmup_count, ...) \
do { \
MEAN_FIELD_PROFILE_SCOPE_WARMUP(label, warmup_count); \
__VA_ARGS__; \
#define MEAN_FIELD_PROFILE_SCOPE_WARMUP(label, warmup_count) \
MEAN_FIELD_PROFILE_SCOPE_IMPL(label, warmup_count, __COUNTER__)
#define MEAN_FIELD_PROFILE_SCOPE(label) MEAN_FIELD_PROFILE_SCOPE_WARMUP(label, 1)
#define MEAN_FIELD_PROFILE_CALL_WARMUP(label, warmup_count, ...) \
do { \
MEAN_FIELD_PROFILE_SCOPE_WARMUP(label, warmup_count); \
__VA_ARGS__; \
} while (false)
#define MEAN_FIELD_PROFILE_CALL(label, ...) \
MEAN_FIELD_PROFILE_CALL_WARMUP(label, 1, __VA_ARGS__)
#define MEAN_FIELD_PROFILE_CALL(label, ...) MEAN_FIELD_PROFILE_CALL_WARMUP(label, 1, __VA_ARGS__)
#define MEAN_FIELD_PROFILE_RESET() \
::mean_field::profiling::Registry::Get().Reset()
#define MEAN_FIELD_PROFILE_EVALUATE_IMPL(label, warmup_count, identifier, ...) \
([&]() -> decltype(auto) { \
MEAN_FIELD_PROFILE_SCOPE_IMPL(label, warmup_count, identifier); \
return (__VA_ARGS__); \
}())
#define MEAN_FIELD_PROFILE_PRINT(communicator) \
::mean_field::profiling::Registry::Get().Print(communicator)
#define MEAN_FIELD_PROFILE_EVALUATE_WARMUP(label, warmup_count, ...) \
MEAN_FIELD_PROFILE_EVALUATE_IMPL(label, warmup_count, __COUNTER__, __VA_ARGS__)
#define MEAN_FIELD_PROFILE_EVALUATE(label, ...) MEAN_FIELD_PROFILE_EVALUATE_WARMUP(label, 1, __VA_ARGS__)
#define MEAN_FIELD_PROFILE_COUNT_IMPL(label, work_units, identifier) \
do { \
static const ::mean_field::profiling::Region MEAN_FIELD_PROFILE_JOIN(mean_field_profile_counter_, identifier)( \
label \
); \
MEAN_FIELD_PROFILE_JOIN(mean_field_profile_counter_, identifier).AddCount(work_units); \
} while (false)
#define MEAN_FIELD_PROFILE_COUNT(label, work_units) MEAN_FIELD_PROFILE_COUNT_IMPL(label, work_units, __COUNTER__)
#define MEAN_FIELD_PROFILE_RESET() ::mean_field::profiling::Registry::Get().Reset()
#define MEAN_FIELD_PROFILE_PRINT(communicator) ::mean_field::profiling::Registry::Get().Print(communicator)
#define MEAN_FIELD_PROFILE_PRINT_CSV(communicator, stream) \
::mean_field::profiling::Registry::Get().PrintCsv(communicator, stream)
#else
#define MEAN_FIELD_PROFILE_SCOPE_WARMUP(label, warmup_count) ((void)0)
#define MEAN_FIELD_PROFILE_SCOPE(label) ((void)0)
#define MEAN_FIELD_PROFILE_CALL_WARMUP(label, warmup_count, ...) \
do { \
__VA_ARGS__; \
} while (false)
#define MEAN_FIELD_PROFILE_CALL(label, ...) MEAN_FIELD_PROFILE_CALL_WARMUP(label, 1, __VA_ARGS__)
#define MEAN_FIELD_PROFILE_EVALUATE_WARMUP(label, warmup_count, ...) (__VA_ARGS__)
#define MEAN_FIELD_PROFILE_EVALUATE(label, ...) (__VA_ARGS__)
#define MEAN_FIELD_PROFILE_COUNT(label, work_units) ((void)0)
#define MEAN_FIELD_PROFILE_RESET() ((void)0)
#define MEAN_FIELD_PROFILE_PRINT(communicator) ((void)0)
#define MEAN_FIELD_PROFILE_PRINT_CSV(communicator, stream) ((void)0)
#endif