metrale_circuit/memory/
weights.rs1use std::collections::BTreeMap;
20
21use super::MemoryError;
22use super::copies::CopyRule;
23use crate::format::Format;
24use crate::ir::{Circuit, Node, NodeIdx, OpKind, Section};
25
26#[derive(Debug, Clone, PartialEq, Eq)]
28pub struct DerivedCopy {
29 pub rule: String,
31 pub bytes: u64,
33 pub leaked: bool,
35}
36
37#[derive(Debug, Clone, PartialEq, Eq)]
39pub struct NodeWeights {
40 pub node: NodeIdx,
42 pub stored: u64,
44 pub derived: Vec<DerivedCopy>,
46}
47
48pub(crate) fn sections(c: &Circuit) -> Vec<Section> {
50 let mut v = vec![Section::Main; c.nodes.len()];
51 for b in &c.blocks {
52 v[b.first..b.end].iter_mut().for_each(|s| *s = b.section);
53 }
54 v
55}
56
57pub(crate) fn weight_shape(c: &Circuit, n: &Node) -> (u64, u64) {
59 let k = n.inputs.first().map_or(0, |&e| c.edges[e].dim_value);
60 let out = n.outputs.iter().map(|&e| c.edges[e].dim_value).sum();
61 (out, k)
62}
63
64fn copies_per_node(c: &Circuit, n: &Node) -> Result<u64, MemoryError> {
65 match n.op {
66 OpKind::ExpertGateUp | OpKind::ExpertDown => {
67 c.dims
68 .get("experts")
69 .copied()
70 .ok_or_else(|| MemoryError::MissingDim {
71 node: n.id.clone(),
72 dim: "experts".into(),
73 })
74 }
75 _ => Ok(1),
76 }
77}
78
79fn bytes_of(n: &Node, what: &str, v: Option<u64>) -> Result<u64, MemoryError> {
80 v.ok_or_else(|| MemoryError::Size {
81 node: n.id.clone(),
82 what: what.to_string(),
83 })
84}
85
86pub fn node_weights(
90 served: &Circuit,
91 declared: &Circuit,
92 rules: &[CopyRule],
93 settings: &BTreeMap<String, String>,
94) -> Result<Vec<NodeWeights>, MemoryError> {
95 let secs = sections(served);
96 let declared_of: BTreeMap<&str, &Node> =
97 declared.nodes.iter().map(|n| (n.id.as_str(), n)).collect();
98 let mut out = Vec::new();
99 let mut bound: std::collections::BTreeSet<&[String]> = std::collections::BTreeSet::new();
100 for (i, n) in served.nodes.iter().enumerate() {
101 if n.op == OpKind::Embed {
102 if secs[i] != Section::Main {
103 continue;
104 }
105 let vocab = *served
106 .dims
107 .get("vocab")
108 .ok_or_else(|| MemoryError::MissingDim {
109 node: n.id.clone(),
110 dim: "vocab".into(),
111 })?;
112 let e = n.outputs.first().map(|&e| &served.edges[e]);
113 let table = e.and_then(|e| e.format.bytes(vocab, e.dim_value));
114 out.push(NodeWeights {
115 node: i,
116 stored: bytes_of(n, "embedding table", table)?,
117 derived: Vec::new(),
118 });
119 continue;
120 }
121 let Some(serve_fmt) = n.weight else { continue };
122 let stored_fmt: Format = declared_of
123 .get(n.id.as_str())
124 .and_then(|d| d.weight)
125 .unwrap_or(serve_fmt);
126 let (o, k) = weight_shape(served, n);
127 let copies = copies_per_node(served, n)?;
128 let one = bytes_of(n, "stored weight", stored_fmt.weight_bytes(o, k))?;
129 let draft_head = secs[i] == Section::Draft && n.op == OpKind::LmHead;
130 let first = !draft_head && (n.binding.is_empty() || bound.insert(n.binding.as_slice()));
131 let stored = match first {
132 true => bytes_of(n, "stored weight", one.checked_mul(copies))?,
133 false => 0,
134 };
135 let mut derived = Vec::new();
136 for r in rules.iter().filter(|r| {
137 r.matches(
138 &served.arch,
139 &n.op,
140 secs[i],
141 stored_fmt,
142 serve_fmt,
143 settings,
144 )
145 }) {
146 let b = r
147 .law
148 .bytes(o, k)
149 .and_then(|b| b.checked_mul(copies))
150 .and_then(|b| b.checked_mul(r.count));
151 derived.push(DerivedCopy {
152 rule: r.id.clone(),
153 bytes: bytes_of(n, &format!("copy `{}`", r.id), b)?,
154 leaked: r.leaked,
155 });
156 }
157 out.push(NodeWeights {
158 node: i,
159 stored,
160 derived,
161 });
162 }
163 Ok(out)
164}