Skip to content

Commit 61a5e01

Browse files
committed
rust: support tagged run metadata graphs
Summary: This patch adds support for TF 1.x `tagged_run_metadata` events. Because this is a new top-level event type, the change extends into the `run` module as well as `data_compat`, but the changed surface area is still rather small. Test Plan: The graphs demo added in #4469 includes tagged run metadata graphs. They now appear in the graphs dashboard with `--load_fast`, and include compute time information. wchargin-branch: rust-tagged-run-metadata wchargin-source: e8ed2e7af25aba1206bccf77218edaf231b1c858
1 parent a1c4362 commit 61a5e01

File tree

3 files changed

+156
-13
lines changed

3 files changed

+156
-13
lines changed

tensorboard/data/server/data_compat.rs

Lines changed: 60 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -26,13 +26,15 @@ pub(crate) const SCALARS_PLUGIN_NAME: &str = "scalars";
2626
pub(crate) const IMAGES_PLUGIN_NAME: &str = "images";
2727
pub(crate) const AUDIO_PLUGIN_NAME: &str = "audio";
2828
pub(crate) const GRAPHS_PLUGIN_NAME: &str = "graphs";
29+
pub(crate) const GRAPH_TAGGED_RUN_METADATA_PLUGIN_NAME: &str = "graph_tagged_run_metadata";
2930

3031
/// The inner contents of a single value from an event.
3132
///
3233
/// This does not include associated step, wall time, tag, or summary metadata information. Step
3334
/// and wall time are available on every event and just not tracked here. Tag and summary metadata
34-
/// information are materialized on `Event`s whose `oneof what` is `summary`, but implicit for
35-
/// graph defs. See [`GraphDefValue::initial_metadata`] and [`SummaryValue::initial_metadata`] for
35+
/// information are materialized on `Event`s whose `oneof what` is `tagged_run_metadata` or
36+
/// `summary`, but implicit for graph defs. See [`GraphDefValue::initial_metadata`],
37+
/// [`TaggedRunMetadataValue::initial_metadata`], and [`SummaryValue::initial_metadata`] for
3638
/// type-specific helpers to determine summary metadata given appropriate information.
3739
///
3840
/// This is kept as close as possible to the on-disk event representation, since every record in
@@ -46,18 +48,20 @@ pub(crate) const GRAPHS_PLUGIN_NAME: &str = "graphs";
4648
#[derive(Debug)]
4749
pub enum EventValue {
4850
GraphDef(GraphDefValue),
51+
TaggedRunMetadata(TaggedRunMetadataValue),
4952
Summary(SummaryValue),
5053
}
5154

5255
impl EventValue {
5356
/// Consumes this event value and enriches it into a scalar.
5457
///
5558
/// This supports `simple_value` (TF 1.x) summaries as well as rank-0 tensors of type
56-
/// `DT_FLOAT`. Returns `DataLoss` if the value is a `GraphDef`, is an unsupported summary, or
57-
/// is a tensor of the wrong rank.
59+
/// `DT_FLOAT`. Returns `DataLoss` if the value is a `GraphDef`, a tagged run metadata proto,
60+
/// an unsupported summary, or a tensor of the wrong rank.
5861
pub fn into_scalar(self) -> Result<ScalarValue, DataLoss> {
5962
let value_box = match self {
6063
EventValue::GraphDef(_) => return Err(DataLoss),
64+
EventValue::TaggedRunMetadata(_) => return Err(DataLoss),
6165
EventValue::Summary(SummaryValue(v)) => v,
6266
};
6367
match *value_box {
@@ -72,16 +76,20 @@ impl EventValue {
7276

7377
/// Consumes this event value and enriches it into a blob sequence.
7478
///
75-
/// For now, this supports `GraphDef`s, summaries with `image` or `audio`, or summaries with
76-
/// `tensor` set to a rank-1 tensor of type `DT_STRING`. If the summary metadata indicates that
77-
/// this is audio data, `tensor` may also be a string tensor of shape `[k, 2]`, in which case
78-
/// the second axis is assumed to represent string labels and is dropped entirely.
79+
/// For now, this supports `GraphDef`s, tagged run metadata protos, summaries with `image` or
80+
/// `audio`, or summaries with `tensor` set to a rank-1 tensor of type `DT_STRING`. If the
81+
/// summary metadata indicates that this is audio data, `tensor` may also be a string tensor of
82+
/// shape `[k, 2]`, in which case the second axis is assumed to represent string labels and is
83+
/// dropped entirely.
7984
pub fn into_blob_sequence(
8085
self,
8186
metadata: &pb::SummaryMetadata,
8287
) -> Result<BlobSequenceValue, DataLoss> {
8388
match self {
8489
EventValue::GraphDef(GraphDefValue(blob)) => Ok(BlobSequenceValue(vec![blob])),
90+
EventValue::TaggedRunMetadata(TaggedRunMetadataValue(run_metadata)) => {
91+
Ok(BlobSequenceValue(vec![run_metadata]))
92+
}
8593
EventValue::Summary(SummaryValue(value_box)) => match *value_box {
8694
pb::summary::value::Value::Image(im) => {
8795
let w = format!("{}", im.width).into_bytes();
@@ -155,6 +163,12 @@ fn tensor_proto_to_scalar(tp: &pb::TensorProto) -> Option<f32> {
155163
/// plugin metadata, but these are not materialized.
156164
pub struct GraphDefValue(pub Vec<u8>);
157165

166+
/// A value from an `Event` whose `tagged_run_metadata` field is set.
167+
///
168+
/// This contains only the `run_metadata` from the event (not the tag). This itself represents the
169+
/// encoding of a `RunMetadata` proto, but that is deserialized at the plugin level.
170+
pub struct TaggedRunMetadataValue(pub Vec<u8>);
171+
158172
/// A value from an `Event` whose `summary` field is set.
159173
///
160174
/// This contains a [`summary::value::Value`], which represents the underlying `oneof value` field
@@ -183,6 +197,17 @@ impl GraphDefValue {
183197
}
184198
}
185199

200+
impl TaggedRunMetadataValue {
201+
/// Determines the metadata for a time series whose first event is a
202+
/// [`TaggedRunMetadata`][`EventValue::TaggedRunMetadata`].
203+
pub fn initial_metadata() -> Box<pb::SummaryMetadata> {
204+
blank(
205+
GRAPH_TAGGED_RUN_METADATA_PLUGIN_NAME,
206+
pb::DataClass::BlobSequence,
207+
)
208+
}
209+
}
210+
186211
impl SummaryValue {
187212
/// Determines the metadata for a time series given its first event.
188213
///
@@ -237,6 +262,14 @@ impl Debug for GraphDefValue {
237262
}
238263
}
239264

265+
impl Debug for TaggedRunMetadataValue {
266+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
267+
f.debug_tuple("TaggedRunMetadataValue")
268+
.field(&format_args!("<{} bytes>", self.0.len()))
269+
.finish()
270+
}
271+
}
272+
240273
/// Creates a summary metadata value with plugin name and data class, but no other contents.
241274
fn blank(plugin_name: &str, data_class: pb::DataClass) -> Box<pb::SummaryMetadata> {
242275
Box::new(pb::SummaryMetadata {
@@ -518,6 +551,16 @@ mod tests {
518551
assert_eq!(md.data_class, i32::from(pb::DataClass::BlobSequence));
519552
}
520553

554+
#[test]
555+
fn test_metadata_tagged_run_metadata() {
556+
let md = TaggedRunMetadataValue::initial_metadata();
557+
assert_eq!(
558+
&md.plugin_data.unwrap().plugin_name,
559+
GRAPH_TAGGED_RUN_METADATA_PLUGIN_NAME
560+
);
561+
assert_eq!(md.data_class, i32::from(pb::DataClass::BlobSequence));
562+
}
563+
521564
#[test]
522565
fn test_metadata_tf1x_image() {
523566
let v = SummaryValue(Box::new(Value::Image(pb::summary::Image {
@@ -637,6 +680,15 @@ mod tests {
637680
);
638681
}
639682

683+
#[test]
684+
fn test_enrich_tagged_run_metadata() {
685+
let v = EventValue::TaggedRunMetadata(TaggedRunMetadataValue(vec![1, 2, 3, 4]));
686+
assert_eq!(
687+
v.into_blob_sequence(GraphDefValue::initial_metadata().as_ref()),
688+
Ok(BlobSequenceValue(vec![vec![1, 2, 3, 4]]))
689+
);
690+
}
691+
640692
#[test]
641693
fn test_enrich_tf1x_image() {
642694
let v = SummaryValue(Box::new(Value::Image(pb::summary::Image {

tensorboard/data/server/run.rs

Lines changed: 47 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ use std::path::PathBuf;
2323
use std::sync::RwLock;
2424

2525
use crate::commit;
26-
use crate::data_compat::{EventValue, GraphDefValue, SummaryValue};
26+
use crate::data_compat::{EventValue, GraphDefValue, SummaryValue, TaggedRunMetadataValue};
2727
use crate::event_file::EventFileReader;
2828
use crate::proto::tensorboard as pb;
2929
use crate::reservoir::StageReservoir;
@@ -277,6 +277,21 @@ fn read_event(
277277
};
278278
ts.rsv.offer(step, sv);
279279
}
280+
Some(pb::event::What::TaggedRunMetadata(trm_proto)) => {
281+
let sv = StageValue {
282+
wall_time,
283+
payload: EventValue::GraphDef(GraphDefValue(trm_proto.run_metadata)),
284+
};
285+
use std::collections::hash_map::Entry;
286+
let ts = match time_series.entry(Tag(trm_proto.tag)) {
287+
Entry::Occupied(o) => o.into_mut(),
288+
Entry::Vacant(v) => {
289+
let metadata = TaggedRunMetadataValue::initial_metadata();
290+
v.insert(StageTimeSeries::new(metadata))
291+
}
292+
};
293+
ts.rsv.offer(step, sv);
294+
}
280295
Some(pb::event::What::Summary(sum)) => {
281296
for mut summary_pb_value in sum.value {
282297
let summary_value = match summary_pb_value.value {
@@ -346,6 +361,12 @@ mod test {
346361
WallTime::new(1235.0).unwrap(),
347362
b"<sample model graph>".to_vec(),
348363
)?;
364+
f1.write_tagged_run_metadata(
365+
&Tag("step0000".to_string()),
366+
Step(0),
367+
WallTime::new(1235.0).unwrap(),
368+
b"<sample run metadata>".to_vec(),
369+
)?;
349370
f1.write_scalar(&tag, Step(0), WallTime::new(1235.0).unwrap(), 0.25)?;
350371
f1.write_scalar(&tag, Step(1), WallTime::new(1236.0).unwrap(), 0.50)?;
351372
f1.write_scalar(&tag, Step(2), WallTime::new(1237.0).unwrap(), 0.75)?;
@@ -404,11 +425,9 @@ mod test {
404425
]
405426
);
406427

428+
assert_eq!(run_data.blob_sequences.len(), 2);
429+
407430
let run_graph_tag = Tag(GraphDefValue::TAG_NAME.to_string());
408-
assert_eq!(
409-
run_data.blob_sequences.keys().collect::<Vec<_>>(),
410-
vec![&run_graph_tag]
411-
);
412431
let graph_ts = run_data.blob_sequences.get(&run_graph_tag).unwrap();
413432
assert_eq!(
414433
*graph_ts.metadata,
@@ -430,6 +449,29 @@ mod test {
430449
)]
431450
);
432451

452+
let run_metadata_tag = Tag("step0000".to_string());
453+
let run_metadata_ts = run_data.blob_sequences.get(&run_metadata_tag).unwrap();
454+
assert_eq!(
455+
*run_metadata_ts.metadata,
456+
pb::SummaryMetadata {
457+
plugin_data: Some(pb::summary_metadata::PluginData {
458+
plugin_name: crate::data_compat::GRAPH_TAGGED_RUN_METADATA_PLUGIN_NAME
459+
.to_string(),
460+
..Default::default()
461+
}),
462+
data_class: pb::DataClass::BlobSequence.into(),
463+
..Default::default()
464+
}
465+
);
466+
assert_eq!(
467+
run_metadata_ts.valid_values().collect::<Vec<_>>(),
468+
vec![(
469+
Step(0),
470+
WallTime::new(1235.0).unwrap(),
471+
&commit::BlobSequenceValue(vec![b"<sample run metadata>".to_vec()])
472+
)]
473+
);
474+
433475
Ok(())
434476
}
435477
}

tensorboard/data/server/writer.rs

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,27 @@ pub trait SummaryWriteExt: Write {
6565
};
6666
self.write_event(&event)
6767
}
68+
69+
/// Writes a TFRecord containing a TF 1.x `tagged_run_metadata` event.
70+
fn write_tagged_run_metadata(
71+
&mut self,
72+
tag: &Tag,
73+
step: Step,
74+
wt: WallTime,
75+
run_metadata: Vec<u8>,
76+
) -> std::io::Result<()> {
77+
let event = pb::Event {
78+
step: step.0,
79+
wall_time: wt.into(),
80+
what: Some(pb::event::What::TaggedRunMetadata(pb::TaggedRunMetadata {
81+
tag: tag.0.clone(),
82+
run_metadata,
83+
..Default::default()
84+
})),
85+
..Default::default()
86+
};
87+
self.write_event(&event)
88+
}
6889
}
6990

7091
impl<W: Write> SummaryWriteExt for W {}
@@ -158,4 +179,32 @@ mod tests {
158179
};
159180
assert_eq!(event, &expected);
160181
}
182+
183+
#[test]
184+
fn test_tagged_run_metadata_roundtrip() {
185+
let mut cursor = Cursor::new(Vec::<u8>::new());
186+
cursor
187+
.write_tagged_run_metadata(
188+
&Tag("step0000".to_string()),
189+
Step(777),
190+
WallTime::new(1234.5).unwrap(),
191+
b"my run metadata".to_vec(),
192+
)
193+
.unwrap();
194+
cursor.set_position(0);
195+
let events = read_all_events(cursor).unwrap();
196+
assert_eq!(events.len(), 1);
197+
198+
let event = &events[0];
199+
let expected = pb::Event {
200+
step: 777,
201+
wall_time: 1234.5,
202+
what: Some(pb::event::What::TaggedRunMetadata(pb::TaggedRunMetadata {
203+
tag: "step0000".to_string(),
204+
run_metadata: b"my run metadata".to_vec(),
205+
})),
206+
..Default::default()
207+
};
208+
assert_eq!(event, &expected);
209+
}
161210
}

0 commit comments

Comments
 (0)