1/* @ajax/mesh-abi-node-load: Node load records (NL_*): how much of the GPU the nodes got, measured from inside.
2 *
3 * Part of alpha-c-mesh, a C port of alpha-rust-mesh (README.md says from where). */
4#ifndef MESH_ABI_NODE_LOAD_H
5#define MESH_ABI_NODE_LOAD_H
67#include <stdbool.h>
8#include <stddef.h>
9#include <stdint.h>
10#include "mesh/abi/node.h"
11#include "mesh/abi/fields.h"
1213/* ---------------------------------------------------------------- node load records */
14/* How much of the GPU meshd's work actually gets, measured from inside it.
15 * CUDA (mesh-rt): a node's idle poll loop reads %globaltimer every turn
16 * (a turn is ~1-5 us); a gap over GAP_NS between two turns is time the
17 * context was off the GPU (time-sliced out by another context: the driver's
18 * slices are ~2 ms). The record sums the wall time observed and the gaps, so
19 * share = 1 - lost / observed. Only idle nodes observe: a node always in a
20 * task observes nothing (its tasks just take longer). Metal (the lease
21 * lanes, node 0's record): observed = commit -> completed of each command
22 * buffer, lost = that minus its GPU time (GPUEndTime - GPUStartTime), gaps =
23 * command buffers, NL_QUEUE_US = commit -> GPUStartTime. Written by the node
24 * (lane 0) or the lane only; meshd reads deltas. They persist across
25 * relaunches (meshd zeroes them when a slot is loaded). */
26#define MESH_NODE_LOAD 0x40000
27#define MESH_NL_STRIDE 0x20
28#define MESH_NL_OBS_NS 0x00 /* u64 wall ns observed */
29#define MESH_NL_LOST_NS 0x08 /* u64 of those, ns lost (CUDA: in gaps > GAP_NS; Metal: round trip - GPU time) */
30#define MESH_NL_GAPS 0x10 /* u32 gaps (CUDA) / command buffers (Metal) */
31#define MESH_NL_MAX_GAP_NS 0x14 /* u32 the longest gap / lost time of one command buffer in window NL_WIN */
32#define MESH_NL_WIN 0x18 /* u32 the H_LOAD_WIN value NL_MAX_GAP_NS belongs to */
33#define MESH_NL_QUEUE_US 0x1c /* u32 Metal: commit -> GPU start, us, summed (wraps; deltas); CUDA 0 */
34/* The shortest gap counted as time off the GPU (the probe's threshold: an
35 * idle turn never takes this long, a time slice always does). */
36#define MESH_GAP_NS 20000u
37#define MESH_NODE_LOAD_FIELDS_LEN 6
38extern const mesh_field MESH_NODE_LOAD_FIELDS[6];
3940/* Node n of slot s's load record. */
41static inline size_t mesh_node_load(uint32_t slot, uint32_t node) {
42 return MESH_NODE_LOAD + MESH_NL_STRIDE * (MESH_PROG_MAX_NODES * (size_t)slot + (size_t)node);
43}
4445/* The Rust const asserts that only involve this package. */
46_Static_assert((MESH_NL_OBS_NS % 8 == 0), "NL_OBS_NS.is_multiple_of(8)");
47_Static_assert((MESH_NL_LOST_NS % 8 == 0), "NL_LOST_NS.is_multiple_of(8)");
4849#endif