Skip to main content
Version: 3.0.0 (experimental)

Core API

dmr.h is the only header an application needs to include. It is a barrel: it pulls in the whole public API and wraps it in extern "C" so C++ callers can use it directly.

#include "dmr.h"
SectionWhat it covers
ConfigurationCompile-time defaults and their environment overrides
Status codesDMRStatus, returned by setters, control calls, and the analytics API
AnalyticsEvent constants, DMRAnalytics, metrics API
Sizing and controlHow big the next expand/shrink is, and state queries
PoliciesDMRPolicy, decision helpers, dmr_set_policy
Lifecycledmr_init / check / reconfigure / finalize, DMR_AUTO

The built-in policies are the one exception to the single include. Their constructors sit outside the barrel, because the code behind them is optional at build time, so reaching them takes one more header. See Policy Headers.


Configuration​

Compile-time defaults, all settable as CMake variables of the same name. Most can also be overridden by an environment variable of the same name at launch; where a runtime setter exists it wins over both.

MacroDefaultOverridable at runtime byDescription
DMR_DEBUG_LEVEL0environment0 = off, 1 = rank 0 only, 2 = all ranks
DMR_PRINT_ANALYTICS0environmentPrint an analytics line at each reconfiguration
DMR_NODES_IN_EXPAND1environment, dmr_set_nodes_next_expand()Nodes added per expand
DMR_NODES_IN_SHRINK1environment, dmr_set_nodes_next_shrink()Nodes removed per shrink
DMR_PROCS_PER_NODE1environment, dmr_set_ppn_next_expand()Processes spawned per added node
DMR_DEFAULT_INHIBITOR0environment, dmr_set_reconf_step_inhibitor()Skip N of every N+1 dmr_check calls
DMR_BLOCKING_REQ0compile time onlyBlock in dmr_check until resources are acquired
DMR_CHECKPOINT_RESTART1compile time only0 uses DMR_INTERCOMM instead of checkpoint files
DMR_JOBS_CAN_SHRINK1compile time onlyAllow shrinking Slurm jobs
DMR_JOBS_CAN_GROW0compile time onlyAllow growing Slurm jobs (needs DMR_JOBS_CAN_SHRINK=1)
DMR_SKIP_SSH_CHECK0compile time onlySkip the SSH health check on new nodes
DMR_SSH_CHECK_TIMEOUT20compile time onlySeconds to wait for that health check
CUSTOM_SLURM_BIN_PREFIX""compile time onlyWhere expander jobs look for scontrol

The remaining macros are policy fallbacks. DMR itself never reads them: each built-in policy resolves them in its constructor, from the environment first and from the macro otherwise.

MacroDefaultRead by
DMR_DEFAULT_POLICY_MIN1round, CE
DMR_DEFAULT_POLICY_MAX1round, CE
DMR_DEFAULT_POLICY_STRIDE2round
DMR_TALP_TARGET_CE0.8CE
DMR_TALP_SENSITIVITY15CE

See Configuration for the full CMake and environment reference.


Status codes​

typedef enum DMRStatusEnum {
DMR_SUCCESS = 0,
DMR_ERROR_STATUS,
DMR_ERROR_UNINITIALIZED,
DMR_ERROR_NOT_ROOT,
DMR_ERROR_ARG_NULL,
DMR_ERROR_BAD_ARGS,
DMR_ERROR_UNSUPPORTED,
DMR_ERROR_OUT_OF_MEM,
} DMRStatus;
ValueMeaning
DMR_SUCCESSSuccess
DMR_ERROR_STATUSUnspecified failure
DMR_ERROR_UNINITIALIZEDdmr_init has not been called yet
DMR_ERROR_NOT_ROOTCalled from a rank other than 0, where only rank 0 is allowed
DMR_ERROR_ARG_NULLA required argument was NULL
DMR_ERROR_BAD_ARGSAn argument was rejected
DMR_ERROR_UNSUPPORTEDNot supported in the current state or build
DMR_ERROR_OUT_OF_MEMOut of memory

The lifecycle functions return DMRAction instead of a status code.


Analytics​

DMRAnalytics​

typedef struct {
double event_time; // Unix timestamp of the event
const char *function; // DMR function that emitted it
const char *event; // Event identifier (DMR_EVENT_* or custom)
int world_size; // MPI processes in the current MPI_COMM_WORLD
int node_count; // Nodes in the current MPI_COMM_WORLD
double reconfiguration_time; // Seconds taken by the last reconfiguration (-1 if N/A)
double communication_efficiency; // Last TALP accumulated CE (-1 if N/A)
int pending_nodes; // Nodes requested but not yet secured
} DMRAnalytics;

Called from a non-root rank, dmr_get_analytics leaves the reconfiguration-specific fields (reconfiguration_time, pending_nodes) at -1.

Functions​

DMRStatus dmr_get_analytics(DMRAnalytics *analytics);
DMRStatus dmr_create_custom_analytics_event(char const *event, DMRAnalytics **analytics_out);
DMRStatus dmr_destroy_custom_analytics_event(DMRAnalytics *analytics);
DMRStatus dmr_print_analytics_from(DMRAnalytics const *analytics_in);

A struct returned by dmr_create_custom_analytics_event is a snapshot, not a live view, and must be released with dmr_destroy_custom_analytics_event. The custom event string must not collide with a reserved DMR_EVENT_* constant.

Event constants​

ConstantWhen emitted
DMR_EVENT_NONENo event yet
DMR_EVENT_INIT_COMPLETEdmr_init completed
DMR_EVENT_CHECK_CALLEDdmr_check was called
DMR_EVENT_STAY_CURRENTThe configuration is left unchanged
DMR_EVENT_START_EXPAND_SLURMResources requested from Slurm
DMR_EVENT_START_EXPAND_MPIMPI expansion started
DMR_EVENT_START_SHRINKShrink triggered
DMR_EVENT_DATA_REDIST_COMPLETEData redistribution finished
DMR_EVENT_TALP_CHECK_CE_ACCTALP CE check performed
DMR_EVENT_LAST_FINALIZEdmr_finalize called outside a reconfiguration

See Analytics for usage.


Sizing and control​

How big the next reconfiguration is, plus the queries a decision needs. Every value set here applies to the next reconfiguration only and resets afterwards.

Sizing setters (rank 0 only)​

Non-root callers get DMR_ERROR_NOT_ROOT and change nothing.

DMRStatus dmr_set_nodes_next_expand(int nodes);
DMRStatus dmr_set_procs_next_expand(int procs); // exact total across the new nodes
DMRStatus dmr_set_ppn_next_expand(int ppn); // processes per node; overrides procs
DMRStatus dmr_set_nodes_next_shrink(int nodes);
DMRStatus dmr_set_procs_next_shrink(int procs);
DMRStatus dmr_set_jobs_next_shrink(int jobs); // remove N whole expansion jobs

Sizing getters (rank 0 only)​

Return -1 on error, including when called from a non-root rank.

int dmr_get_nodes_next_expand(void);
int dmr_get_procs_next_expand(void);
int dmr_get_nodes_next_shrink(void);
int dmr_get_procs_next_shrink(void);

State queries​

Callable from any rank.

int dmr_get_current_node_count(void); // nodes in the current MPI_COMM_WORLD
int dmr_get_reconfig_count(void); // reconfigurations since launch; valid even before dmr_init
int dmr_get_active_expansions(void); // expansion jobs currently active
int dmr_pending_expansion(void); // 1 if an expansion is pending

Expansion control and throttling​

DMRStatus dmr_cancel_expansion(void); // collective
DMRStatus dmr_set_reconf_step_inhibitor(int steps);

dmr_cancel_expansion is collective and only valid while dmr_pending_expansion() returns 1. The inhibitor makes DMR ignore N out of every N+1 dmr_check calls; it has no effect on a reconfiguration already underway.


Policies​

The policy interface: a policy is an object you register once, and DMR evaluates it whenever you call dmr_check(USE_POLICY).

Types​

typedef enum DMRPolicyOpEnum {
DMR_POLICY_STAY = 0,
DMR_POLICY_EXPAND,
DMR_POLICY_SHRINK
} DMRPolicyOp;

typedef struct {
DMRPolicyOp operation;
int nodes; // delta, positive
int processes; // 0 = let DMR size it
} DMRPolicySuggestion;

typedef struct {
int current_nodes;
int reconfig_count;
int nodes_in_expand;
int nodes_in_shrink;
int procs_in_expand;
int procs_in_shrink;
void *internal_dmr_state;
void *internal_controller_state;
} DMRPolicyContext;

struct DMRPolicyStruct {
char const *name;
size_t state_size;
void *state;
DMRPolicyPopulateFn populate; // optional: gather external data, all ranks
DMRPolicyRunFn run; // required: pure decision, all ranks
DMRPolicySaveFn save; // optional, never called by DMR
DMRPolicyLoadFn load; // optional, never called by DMR
DMRPolicyDestroyFn destroy; // optional, never called by DMR
};

Registration​

DMRStatus dmr_set_policy(DMRPolicy *policy);

Collective: all ranks must call it, in the same order, with no dmr_check in between. DMR does not take ownership of the pointer: keep the object alive for as long as it may be evaluated. Passing NULL unregisters the policy, and dmr_check(USE_POLICY) then resolves to SHOULD_STAY.

Decision helpers​

DMRPolicySuggestion dmr_policy_stay(void);
DMRPolicySuggestion dmr_policy_expand_fixed(int nodes, int processes);
DMRPolicySuggestion dmr_policy_shrink_fixed(int nodes, int processes);
DMRPolicySuggestion dmr_policy_expand_to(int target_nodes, int processes, DMRPolicyContext const *context);
DMRPolicySuggestion dmr_policy_shrink_to(int target_nodes, int processes, DMRPolicyContext const *context);

_fixed takes a delta, _to an absolute target node count. All of them degrade to dmr_policy_stay() when the delta would be zero or negative, or the context is NULL.

How the two callbacks are invoked is described in Policies Overview; writing your own policy is covered in Custom Policies.


Lifecycle​

DMRSuggestion​

Passed to dmr_check to say how this iteration's size is decided.

ValueDescription
USE_POLICYEvaluate the policy registered with dmr_set_policy
SHOULD_EXPANDExpand by dmr_get_nodes_next_expand() nodes
SHOULD_SHRINKShrink by dmr_get_nodes_next_shrink() nodes
SHOULD_STAYDo not reconfigure this iteration

The per-policy enum values of 2.x (ROUND_POLICY, CE_POLICY, SLURM4DMR_*, …) are gone; policies are objects now. See Policies Overview.

DMRAction​

Returned by dmr_init, dmr_check, dmr_reconfigure, and dmr_finalize, telling the calling rank what to do next.

ValueMeaning
DMR_NO_ACTIONNo action required
DMR_RECONFCall dmr_reconfigure()
DMR_RESTART_RECONFLoad checkpoint/data, then call dmr_reconfigure()
DMR_REDIST_FINALIZESave/send data, then call dmr_finalize() (rank exits)
DMR_FINALIZECall dmr_finalize() (rank exits)
DMR_CLEANUPOptional cleanup, rank continues
DMR_ERRORAn error occurred

DMR_INTERCOMM​

extern MPI_Comm DMR_INTERCOMM;

Intercommunicator connecting the old and new processes during a reconfiguration. Only touch it when a DMRAction told you to (DMR_RESTART_RECONF or DMR_REDIST_FINALIZE) and dmr_intercomm_available() returns 1, which requires a build with DMR_CHECKPOINT_RESTART=0. See Data Redistribution.

Functions​

DMRAction dmr_init(int argc, char *argv[]);
DMRAction dmr_check(DMRSuggestion suggested_reconfiguration);
DMRAction dmr_reconfigure(void);
DMRAction dmr_finalize(void);
int dmr_intercomm_available(void);
DMRAction dmr_get_last_action(void);

dmr_init: call immediately after MPI_Init, passing argc/argv untouched (DMR forwards them to spawned processes). Collective. Returns DMR_NO_ACTION on first launch, DMR_RESTART_RECONF on a process spawned by an expansion.

dmr_check: evaluate the suggestion (or the active policy, with USE_POLICY) and act on any pending reconfiguration. Call it once per iteration of your main loop. Collective.

dmr_reconfigure: perform the reconfiguration. Only valid after dmr_check or dmr_init returned DMR_RECONF or DMR_RESTART_RECONF. Handled for you by DMR_AUTO.

dmr_finalize: shut DMR down. Call it before MPI_Finalize. Not collective, and it is a termination point: it finalizes MPI and exits the rank, so no DMR call can follow it on that rank.

DMR_AUTO​

DMR_AUTO(the_action, redist_func, restart_func, finalize_func)

Expands to a switch over the returned action and calls the right callback for you, including dmr_reconfigure() and dmr_finalize(). Pass (void)NULL for a callback you do not need.

DMR_AUTO(dmr_init(argc, argv), (void)NULL, load(), cleanup());
DMR_AUTO(dmr_check(USE_POLICY), save(), (void)NULL, cleanup());
DMR_AUTO(dmr_finalize(), (void)NULL, (void)NULL, cleanup());
The macro is a possible termination point

On a leaving rank DMR_AUTO calls dmr_finalize(), which finalizes MPI and exits. Never call MPI_Finalize from finalize_func.

Full semantics in Reconfiguration Handling.