Skip to content

Commit c953942

Browse files
committed
tracing: significantly improve code generation at trace points
`ValueSet`s contain both a `FieldSet` reference and a slice of (`&Field`, `Option<&dyn Value>`) pairs. In cases where `ValueSet`s are generated via documented interfaces (specifically, `tracing::event!` and other macros), the `Field` references are redundant, because the `ValueSet` contains a value slot for every field (either a value or `None`), in the correct order. As a result, the code generated by the macros is terrible--it must put a `Field` on the stack for each field--that's 32 bytes per field! This is a lot of work for apparently no purpose at runtime, and it can't be moved into a read-only data section since it's intermixed with dynamic data. Fix this by adding a variant of `ValueSet` that skips the `Field` references, knowing that it represents the full set of fields. Keep the old kind of `ValueSet`, too--it's still needed by `Span::record`, by old versions of crates, and potentially by third-party crates using undocumented methods such as `FieldSet::value_set`. In some adhoc tests on x86_64 Linux, this reduces the code size as follows: * One-field event: 258 bytes to 189 bytes, 25% reduction. * Five-field event: 638 bytes to 276 bytes, **57%** reduction. * In a larger project with lots of events, ~5% reduction in .text section.
1 parent c297a37 commit c953942

File tree

2 files changed

+128
-121
lines changed

2 files changed

+128
-121
lines changed

tracing-core/src/field.rs

Lines changed: 73 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -165,10 +165,18 @@ pub struct FieldSet {
165165

166166
/// A set of fields and values for a span.
167167
pub struct ValueSet<'a> {
168-
values: &'a [(&'a Field, Option<&'a (dyn Value + 'a)>)],
168+
values: Values<'a>,
169169
fields: &'a FieldSet,
170170
}
171171

172+
enum Values<'a> {
173+
/// A set of field-value pairs. Fields may be for the wrong field set, some
174+
/// fields may be missing, and fields may be in any order.
175+
Mixed(&'a [(&'a Field, Option<&'a (dyn Value + 'a)>)]),
176+
/// A list of values corresponding exactly to the fields in a `FieldSet`.
177+
All(&'a [Option<&'a (dyn Value + 'a)>]),
178+
}
179+
172180
/// An iterator over a set of fields.
173181
#[derive(Debug)]
174182
pub struct Iter {
@@ -922,7 +930,18 @@ impl FieldSet {
922930
{
923931
ValueSet {
924932
fields: self,
925-
values: values.borrow(),
933+
values: Values::Mixed(values.borrow()),
934+
}
935+
}
936+
937+
/// Returns a new `ValueSet` for `values`. These values must exactly
938+
/// correspond to the fields in this `FieldSet`.
939+
#[doc(hidden)]
940+
pub fn value_set_all<'v>(&'v self, values: &'v [Option<&'v (dyn Value + 'v)>]) -> ValueSet<'v> {
941+
debug_assert_eq!(values.len(), self.len());
942+
ValueSet {
943+
fields: self,
944+
values: Values::All(values),
926945
}
927946
}
928947

@@ -1033,13 +1052,24 @@ impl ValueSet<'_> {
10331052
///
10341053
/// [visitor]: Visit
10351054
pub fn record(&self, visitor: &mut dyn Visit) {
1036-
let my_callsite = self.callsite();
1037-
for (field, value) in self.values {
1038-
if field.callsite() != my_callsite {
1039-
continue;
1055+
match self.values {
1056+
Values::Mixed(values) => {
1057+
let my_callsite = self.callsite();
1058+
for (field, value) in values {
1059+
if field.callsite() != my_callsite {
1060+
continue;
1061+
}
1062+
if let Some(value) = *value {
1063+
value.record(field, visitor);
1064+
}
1065+
}
10401066
}
1041-
if let Some(value) = value {
1042-
value.record(field, visitor);
1067+
Values::All(values) => {
1068+
for (field, value) in self.fields.iter().zip(values.iter()) {
1069+
if let Some(value) = *value {
1070+
value.record(&field, visitor);
1071+
}
1072+
}
10431073
}
10441074
}
10451075
}
@@ -1050,28 +1080,44 @@ impl ValueSet<'_> {
10501080
/// [visitor]: Visit
10511081
/// [`ValueSet::record()`]: ValueSet::record()
10521082
pub fn len(&self) -> usize {
1053-
let my_callsite = self.callsite();
1054-
self.values
1055-
.iter()
1056-
.filter(|(field, _)| field.callsite() == my_callsite)
1057-
.count()
1083+
match &self.values {
1084+
Values::Mixed(values) => {
1085+
let my_callsite = self.callsite();
1086+
values
1087+
.iter()
1088+
.filter(|(field, _)| field.callsite() == my_callsite)
1089+
.count()
1090+
}
1091+
Values::All(values) => values.len(),
1092+
}
10581093
}
10591094

10601095
/// Returns `true` if this `ValueSet` contains a value for the given `Field`.
10611096
pub(crate) fn contains(&self, field: &Field) -> bool {
1062-
field.callsite() == self.callsite()
1063-
&& self
1064-
.values
1097+
if field.callsite() != self.callsite() {
1098+
return false;
1099+
}
1100+
match &self.values {
1101+
Values::Mixed(values) => values
10651102
.iter()
1066-
.any(|(key, val)| *key == field && val.is_some())
1103+
.any(|(key, val)| *key == field && val.is_some()),
1104+
Values::All(values) => {
1105+
values[field.i].is_some()
1106+
}
1107+
}
10671108
}
10681109

10691110
/// Returns true if this `ValueSet` contains _no_ values.
10701111
pub fn is_empty(&self) -> bool {
1071-
let my_callsite = self.callsite();
1072-
self.values
1073-
.iter()
1074-
.all(|(key, val)| val.is_none() || key.callsite() != my_callsite)
1112+
match &self.values {
1113+
Values::All(values) => values.iter().all(|v| v.is_none()),
1114+
Values::Mixed(values) => {
1115+
let my_callsite = self.callsite();
1116+
values
1117+
.iter()
1118+
.all(|(key, val)| val.is_none() || key.callsite() != my_callsite)
1119+
}
1120+
}
10751121
}
10761122

10771123
pub(crate) fn field_set(&self) -> &FieldSet {
@@ -1081,30 +1127,17 @@ impl ValueSet<'_> {
10811127

10821128
impl fmt::Debug for ValueSet<'_> {
10831129
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1084-
self.values
1085-
.iter()
1086-
.fold(&mut f.debug_struct("ValueSet"), |dbg, (key, v)| {
1087-
if let Some(val) = v {
1088-
val.record(key, dbg);
1089-
}
1090-
dbg
1091-
})
1092-
.field("callsite", &self.callsite())
1093-
.finish()
1130+
let mut s = f.debug_struct("ValueSet");
1131+
self.record(&mut s);
1132+
s.field("callsite", &self.callsite()).finish()
10941133
}
10951134
}
10961135

10971136
impl fmt::Display for ValueSet<'_> {
10981137
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1099-
self.values
1100-
.iter()
1101-
.fold(&mut f.debug_map(), |dbg, (key, v)| {
1102-
if let Some(val) = v {
1103-
val.record(key, dbg);
1104-
}
1105-
dbg
1106-
})
1107-
.finish()
1138+
let mut s = f.debug_map();
1139+
self.record(&mut s);
1140+
s.finish()
11081141
}
11091142
}
11101143

0 commit comments

Comments
 (0)