skip to content
==> src/load_prog.c <==
1/* load.rs Prog::sample, in the Rust's float widths. */
2#include "mesh/load_prog.h"
3#include "mesh/buf.h"
4#include "mesh/cpuuse.h"
5
6#include <stdlib.h>
7#include <string.h>
8
9mesh_nl mesh_nl_read(mesh_mailbox mb, uint32_t s, uint32_t n, uint32_t *max_gap_ns, uint32_t *win) {
10    size_t r = mesh_node_load(s, n);
11    mesh_nl x = {mesh_mailbox_get64(mb, r + MESH_NL_OBS_NS), mesh_mailbox_get64(mb, r + MESH_NL_LOST_NS), mesh_mailbox_get(mb, r + MESH_NL_GAPS),
12                 mesh_mailbox_get(mb, r + MESH_NL_QUEUE_US)};
13    *max_gap_ns = mesh_mailbox_get(mb, r + MESH_NL_MAX_GAP_NS);
14    *win = mesh_mailbox_get(mb, r + MESH_NL_WIN);
15    return x;
16}
17
18void mesh_prog_sampler_reset(mesh_prog_sampler *p) {
19    for (size_t i = 0; i < p->nwin; i++) free(p->win[i].lat);
20    memset(p, 0, sizeof *p);
21}
22
23static void pf(uint8_t *b, size_t o, float v) { memcpy(b + o, &v, 4); }
24static void pu(uint8_t *b, size_t o, uint32_t v) { for (int i = 0; i < 4; i++) b[o + (size_t)i] = (uint8_t)(v >> (8 * i)); }
25static void pu64(uint8_t *b, size_t o, uint64_t v) { for (int i = 0; i < 8; i++) b[o + (size_t)i] = (uint8_t)(v >> (8 * i)); }
26
27static int cmp_u32(const void *a, const void *b) {
28    uint32_t x = *(const uint32_t *)a, y = *(const uint32_t *)b;
29    return x < y ? -1 : x > y;
30}
31
32void mesh_prog_sample(mesh_prog_sampler *p, mesh_mailbox mb, uint32_t s, double dt, uint32_t win, uint8_t out[MESH_PLOAD_BYTES], mesh_nl *dsum,
33                      uint32_t *max_gap_out) {
34    memset(out, 0, MESH_PLOAD_BYTES);
35    size_t e = mesh_prog_entry(s);
36    uint64_t hash = mesh_mailbox_get64(mb, e + MESH_PT_PTX_HASH);
37    uint32_t loads = mesh_mailbox_get(mb, e + MESH_PT_LOADS);
38    uint32_t nodes = mesh_mailbox_get(mb, e + MESH_PT_NODES);
39    if (nodes > MESH_PROG_MAX_NODES) nodes = MESH_PROG_MAX_NODES;
40    bool fresh = !p->started || hash != p->hash || loads != p->loads || nodes != p->nodes;
41    /* The CPU executors' cost (wasm lanes, native workers): cumulative, baselined at the image's load. */
42    uint32_t exec = mesh_mailbox_get(mb, mesh_prog_ext(s) + MESH_PX_EXEC);
43    bool host = exec == MESH_BACKEND_WASM || exec == MESH_BACKEND_NATIVE;
44    uint64_t cpu, rss;
45    uint32_t units;
46    mesh_slot_use_get(&mesh_cpuuse_slots[s], &cpu, &rss, &units);
47    if (fresh) {
48        mesh_prog_sampler_reset(p);
49        p->started = true;
50        p->hash = hash;
51        p->loads = loads;
52        p->nodes = nodes;
53        p->cpu_base = p->cpu_last = cpu;
54    }
55    uint64_t cpu_d = cpu > p->cpu_last ? cpu - p->cpu_last : 0;
56    if (cpu > p->cpu_last) p->cpu_last = cpu;
57    /* GPU share: the node load records; the longest gap of the window that just closed. */
58    mesh_nl d = {0, 0, 0, 0};
59    uint32_t max_gap = 0;
60    uint64_t busy_ns = 0, busy_tasks = 0;
61    for (uint32_t n = 0; n < nodes; n++) {
62        uint32_t mg, w;
63        mesh_nl now = mesh_nl_read(mb, s, n, &mg, &w);
64        if (w == win && mg > max_gap) max_gap = mg;
65        size_t r = mesh_node_rec(s, n);
66        uint64_t bn = mesh_mailbox_get64(mb, r + MESH_N_BUSY_NS);
67        uint32_t bt = mesh_mailbox_get(mb, r + MESH_N_TASKS);
68        if (!fresh) {
69            mesh_nl dd = mesh_nl_delta(&now, &p->nl[n]);
70            mesh_nl_add(&d, &dd);
71            if (bn >= p->busy_ns[n]) { busy_ns += bn - p->busy_ns[n]; busy_tasks += (uint64_t)(uint32_t)(bt - p->busy_tasks[n]); }
72        }
73        p->nl[n] = now;
74        p->busy_ns[n] = bn;
75        p->busy_tasks[n] = bt;
76    }
77    /* The trace records written since the last sample (at most a ring's worth). */
78    uint32_t tn = mesh_mailbox_get(mb, mesh_prog_ctl(s) + MESH_P_TRACE_N);
79    uint32_t *lat = NULL;
80    size_t nlat = 0;
81    if (!fresh) {
82        uint32_t nw = tn - p->trace_n;
83        if (nw > MESH_TRACE_PER_PROG) nw = MESH_TRACE_PER_PROG;
84        lat = mesh_xmalloc((nw ? nw : 1) * sizeof *lat);
85        for (uint32_t i = tn - nw; i != tn; i++) {
86            size_t r = mesh_trace_rec(s, i);
87            uint32_t w0 = mesh_mailbox_get(mb, r);
88            if ((w0 & 0xffu) != s || (w0 >> 16) == MESH_OP_INIT) continue;
89            lat[nlat++] = mesh_mailbox_get(mb, r + 12) - mesh_mailbox_get(mb, r + 8);
90        }
91    }
92    p->trace_n = tn;
93    uint32_t done = mesh_mailbox_get(mb, mesh_prog_ctl(s) + MESH_P_DONE);
94    uint64_t tasks = fresh ? 0 : (uint64_t)(uint32_t)(done - p->done);
95    p->done = done;
96    if (fresh) {
97        pu(out, MESH_PL_FLAGS, MESH_PLF_LOADED | (host ? MESH_PLF_HOST : 0));
98        if (host) { pu64(out, MESH_PL_RSS, rss); pu(out, MESH_PL_UNITS, units); }
99        free(lat);
100        *dsum = d;
101        *max_gap_out = 0;
102        return;
103    }
104    if (p->nwin == MESH_LOAD_WINDOW) {
105        free(p->win[0].lat);
106        memmove(p->win, p->win + 1, (MESH_LOAD_WINDOW - 1) * sizeof *p->win);
107        p->nwin--;
108    }
109    mesh_prog_window *nwn = &p->win[p->nwin++];
110    nwn->d = d;
111    nwn->lat = lat;
112    nwn->nlat = nlat;
113    nwn->busy_ns = busy_ns;
114    nwn->busy_tasks = busy_tasks;
115    nwn->tasks = tasks;
116    nwn->cpu_d = cpu_d;
117    mesh_nl w = {0, 0, 0, 0};
118    uint64_t wb0 = 0, wb1 = 0, wdone = 0, wcpu = 0;
119    size_t wt = 0, nall = 0;
120    for (size_t i = 0; i < p->nwin; i++) nall += p->win[i].nlat;
121    uint32_t *all = mesh_xmalloc((nall ? nall : 1) * sizeof *all);
122    size_t k = 0;
123    for (size_t i = 0; i < p->nwin; i++) {
124        mesh_prog_window *x = &p->win[i];
125        mesh_nl_add(&w, &x->d);
126        memcpy(all + k, x->lat, x->nlat * sizeof *all);
127        k += x->nlat;
128        wb0 += x->busy_ns;
129        wb1 += x->busy_tasks;
130        wt++;
131        wdone += x->tasks;
132        wcpu += x->cpu_d;
133    }
134    qsort(all, nall, sizeof *all, cmp_u32);
135    if (wb1 > 0) { p->has_task_us = true; p->task_us = (float)((double)wb0 / (double)wb1 / 1e3); }
136    mesh_share one = mesh_share_of(&d), ten = mesh_share_of(&w);
137    uint32_t flags = MESH_PLF_LOADED;
138    if ((one.has_share || ten.has_share) && ten.has_share) {
139        flags |= MESH_PLF_GPU;
140        pf(out, MESH_PL_SHARE, one.has_share ? one.share : ten.share);
141        pf(out, MESH_PL_SHARE10, ten.share);
142        pf(out, MESH_PL_SLICE_US, ten.slice_us);
143        pf(out, MESH_PL_GAP_US, ten.gap_us);
144        pf(out, MESH_PL_QUEUE_US, ten.queue_us);
145    }
146    pf(out, MESH_PL_MAX_GAP_US, (float)max_gap / 1e3f);
147    pf(out, MESH_PL_OBS, (float)((double)d.obs / 1e9 / dt));
148    if (nall) {
149        flags |= MESH_PLF_LAT;
150        pu(out, MESH_PL_LAT_N, (uint32_t)nall);
151        const size_t offs[3] = {MESH_PL_P50_US, MESH_PL_P90_US, MESH_PL_P99_US};
152        const double qs[3] = {0.5, 0.9, 0.99};
153        for (int i = 0; i < 3; i++) pf(out, offs[i], (float)mesh_quantile(all, nall, qs[i]) / 1e3f);
154        pf(out, MESH_PL_MAX_US, (float)all[nall - 1] / 1e3f);
155    }
156    free(all);
157    if (p->has_task_us) { flags |= MESH_PLF_TASK; pf(out, MESH_PL_TASK_US, p->task_us); }
158    pf(out, MESH_PL_TASKS_S, (float)((double)wdone / ((double)wt * dt)));
159    if (nodes > 0) {
160        double b = ((double)busy_ns / 1e9) / ((double)nodes * dt);
161        pf(out, MESH_PL_BUSY, (float)(b < 1.0 ? b : 1.0));
162    }
163    if (host) {
164        flags |= MESH_PLF_HOST;
165        pu64(out, MESH_PL_CPU_NS, cpu > p->cpu_base ? cpu - p->cpu_base : 0);
166        pu64(out, MESH_PL_RSS, rss);
167        pf(out, MESH_PL_CPU, (float)((double)cpu_d / 1e9 / dt));
168        pf(out, MESH_PL_CPU10, (float)((double)wcpu / 1e9 / ((double)wt * dt)));
169        if (wdone > 0) pf(out, MESH_PL_CPU_TASK_US, (float)((double)wcpu / 1e3 / (double)wdone));
170        pu(out, MESH_PL_UNITS, units);
171    }
172    pu(out, MESH_PL_FLAGS, flags);
173    *dsum = d;
174    *max_gap_out = max_gap;
175}