Skip to content

Commit 3accdce

Browse files
committed
subscriber: don't use SmallVecs for filter fields (#1568)
## Motivation The `DirectiveSet` type used in `EnvFilter` and `Targets` uses `SmallVec` to store the filtering directives when the `SmallVec` feature is enabled. This is intended to improve the performance of iterating over small sets of directives, by avoiding a heap pointer dereference. PR #1550 changed the directives themselves to also use `SmallVec` for storing _field_ filters. This was intended to make the same optimization for field filters. However, it had unintended consequences: an empty `SmallVec` is an array of `T` items (plus metadata), while an empty `Vec` is just a couple of words. Since _most_ filters don't have field filters, this meant that we were suddenly using a lot more space to store...nothing. This made `EnvFilter`s _much_ larger, causing problems for some users (see #1567). ## Solution This branch undoes the change to `SmallVec` for field name/value filters. This takes the size of an `EnvFilter` from 5420 bytes back down to 1272 bytes. I also added some tests that just print the size of various `EnvFilter` and `Targets` values. These don't make any assertions, but can be run for development purposes when making changes to these types. Fixes #1567 Signed-off-by: Eliza Weisman <eliza@buoyant.io>
1 parent 10b0c91 commit 3accdce

File tree

4 files changed

+76
-32
lines changed

4 files changed

+76
-32
lines changed

tracing-subscriber/src/filter/directive.rs

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,14 @@ pub struct ParseError {
1313
#[derive(Debug, PartialEq, Eq, Clone)]
1414
pub(crate) struct StaticDirective {
1515
pub(in crate::filter) target: Option<String>,
16-
pub(in crate::filter) field_names: FilterVec<String>,
16+
pub(in crate::filter) field_names: Vec<String>,
1717
pub(in crate::filter) level: LevelFilter,
1818
}
1919

2020
#[cfg(feature = "smallvec")]
21-
pub(in crate::filter) type FilterVec<T> = smallvec::SmallVec<[T; 8]>;
21+
pub(crate) type FilterVec<T> = smallvec::SmallVec<[T; 8]>;
2222
#[cfg(not(feature = "smallvec"))]
23-
pub(in crate::filter) type FilterVec<T> = Vec<T>;
23+
pub(crate) type FilterVec<T> = Vec<T>;
2424

2525
#[derive(Debug, PartialEq, Clone)]
2626
pub(in crate::filter) struct DirectiveSet<T> {
@@ -129,7 +129,7 @@ impl DirectiveSet<StaticDirective> {
129129
impl StaticDirective {
130130
pub(in crate::filter) fn new(
131131
target: Option<String>,
132-
field_names: FilterVec<String>,
132+
field_names: Vec<String>,
133133
level: LevelFilter,
134134
) -> Self {
135135
Self {
@@ -221,7 +221,7 @@ impl Default for StaticDirective {
221221
fn default() -> Self {
222222
StaticDirective {
223223
target: None,
224-
field_names: FilterVec::new(),
224+
field_names: Vec::new(),
225225
level: LevelFilter::ERROR,
226226
}
227227
}
@@ -288,7 +288,7 @@ impl FromStr for StaticDirective {
288288

289289
let mut split = part0.split("[{");
290290
let target = split.next().map(String::from);
291-
let mut field_names = FilterVec::new();
291+
let mut field_names = Vec::new();
292292
// Directive includes fields:
293293
// * `foo[{bar}]=trace`
294294
// * `foo[{bar,baz}]=trace`
@@ -326,12 +326,12 @@ impl FromStr for StaticDirective {
326326
Ok(level) => Self {
327327
level,
328328
target: None,
329-
field_names: FilterVec::new(),
329+
field_names: Vec::new(),
330330
},
331331
Err(_) => Self {
332332
target: Some(String::from(part0)),
333333
level: LevelFilter::TRACE,
334-
field_names: FilterVec::new(),
334+
field_names: Vec::new(),
335335
},
336336
})
337337
}

tracing-subscriber/src/filter/env/directive.rs

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
1-
use super::FilterVec;
2-
pub(crate) use crate::filter::directive::{ParseError, StaticDirective};
1+
pub(crate) use crate::filter::directive::{FilterVec, ParseError, StaticDirective};
32
use crate::filter::{
43
directive::{DirectiveSet, Match},
54
env::{field, FieldMap},
@@ -16,7 +15,7 @@ use tracing_core::{span, Level, Metadata};
1615
#[cfg_attr(docsrs, doc(cfg(feature = "env-filter")))]
1716
pub struct Directive {
1817
in_span: Option<String>,
19-
fields: FilterVec<field::Match>,
18+
fields: Vec<field::Match>,
2019
pub(crate) target: Option<String>,
2120
pub(crate) level: LevelFilter,
2221
}
@@ -216,12 +215,12 @@ impl FromStr for Directive {
216215
FIELD_FILTER_RE
217216
.find_iter(c.as_str())
218217
.map(|c| c.as_str().parse())
219-
.collect::<Result<FilterVec<_>, _>>()
218+
.collect::<Result<Vec<_>, _>>()
220219
})
221-
.unwrap_or_else(|| Ok(FilterVec::new()));
220+
.unwrap_or_else(|| Ok(Vec::new()));
222221
Some((span, fields))
223222
})
224-
.unwrap_or_else(|| (None, Ok(FilterVec::new())));
223+
.unwrap_or_else(|| (None, Ok(Vec::new())));
225224

226225
let level = caps
227226
.name("level")
@@ -244,7 +243,7 @@ impl Default for Directive {
244243
level: LevelFilter::OFF,
245244
target: None,
246245
in_span: None,
247-
fields: FilterVec::new(),
246+
fields: Vec::new(),
248247
}
249248
}
250249
}

tracing-subscriber/src/filter/env/mod.rs

Lines changed: 29 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -118,11 +118,6 @@ thread_local! {
118118

119119
type FieldMap<T> = HashMap<Field, T>;
120120

121-
#[cfg(feature = "smallvec")]
122-
type FilterVec<T> = smallvec::SmallVec<[T; 8]>;
123-
#[cfg(not(feature = "smallvec"))]
124-
type FilterVec<T> = Vec<T>;
125-
126121
/// Indicates that an error occurred while parsing a `EnvFilter` from an
127122
/// environment variable.
128123
#[cfg_attr(docsrs, doc(cfg(all(feature = "env-filter", feature = "std"))))]
@@ -713,4 +708,33 @@ mod tests {
713708
assert_eq!(f1.statics, f2.statics);
714709
assert_eq!(f1.dynamics, f2.dynamics);
715710
}
711+
712+
#[test]
713+
fn size_of_filters() {
714+
fn print_sz(s: &str) {
715+
let filter = s.parse::<EnvFilter>().expect("filter should parse");
716+
println!(
717+
"size_of_val({:?})\n -> {}B",
718+
s,
719+
std::mem::size_of_val(&filter)
720+
);
721+
}
722+
723+
print_sz("info");
724+
725+
print_sz("foo=debug");
726+
727+
print_sz(
728+
"crate1::mod1=error,crate1::mod2=warn,crate1::mod2::mod3=info,\
729+
crate2=debug,crate3=trace,crate3::mod2::mod1=off",
730+
);
731+
732+
print_sz("[span1{foo=1}]=error,[span2{bar=2 baz=false}],crate2[{quux=\"quuux\"}]=debug");
733+
734+
print_sz(
735+
"crate1::mod1=error,crate1::mod2=warn,crate1::mod2::mod3=info,\
736+
crate2=debug,crate3=trace,crate3::mod2::mod1=off,[span1{foo=1}]=error,\
737+
[span2{bar=2 baz=false}],crate2[{quux=\"quuux\"}]=debug",
738+
);
739+
}
716740
}

tracing-subscriber/src/filter/targets.rs

Lines changed: 33 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -361,11 +361,11 @@ mod tests {
361361
assert_eq!(dirs.len(), 2, "\nparsed: {:#?}", dirs);
362362
assert_eq!(dirs[0].target, Some("server".to_string()));
363363
assert_eq!(dirs[0].level, LevelFilter::DEBUG);
364-
assert_eq!(dirs[0].field_names, FilterVec::<String>::default());
364+
assert_eq!(dirs[0].field_names, Vec::<String>::new());
365365

366366
assert_eq!(dirs[1].target, Some("common".to_string()));
367367
assert_eq!(dirs[1].level, LevelFilter::INFO);
368-
assert_eq!(dirs[1].field_names, FilterVec::<String>::default());
368+
assert_eq!(dirs[1].field_names, Vec::<String>::new());
369369
}
370370

371371
fn expect_parse_level_directives(s: &str) {
@@ -374,27 +374,27 @@ mod tests {
374374

375375
assert_eq!(dirs[0].target, Some("crate3::mod2::mod1".to_string()));
376376
assert_eq!(dirs[0].level, LevelFilter::OFF);
377-
assert_eq!(dirs[0].field_names, FilterVec::<String>::default());
377+
assert_eq!(dirs[0].field_names, Vec::<String>::new());
378378

379379
assert_eq!(dirs[1].target, Some("crate1::mod2::mod3".to_string()));
380380
assert_eq!(dirs[1].level, LevelFilter::INFO);
381-
assert_eq!(dirs[1].field_names, FilterVec::<String>::default());
381+
assert_eq!(dirs[1].field_names, Vec::<String>::new());
382382

383383
assert_eq!(dirs[2].target, Some("crate1::mod2".to_string()));
384384
assert_eq!(dirs[2].level, LevelFilter::WARN);
385-
assert_eq!(dirs[2].field_names, FilterVec::<String>::default());
385+
assert_eq!(dirs[2].field_names, Vec::<String>::new());
386386

387387
assert_eq!(dirs[3].target, Some("crate1::mod1".to_string()));
388388
assert_eq!(dirs[3].level, LevelFilter::ERROR);
389-
assert_eq!(dirs[3].field_names, FilterVec::<String>::default());
389+
assert_eq!(dirs[3].field_names, Vec::<String>::new());
390390

391391
assert_eq!(dirs[4].target, Some("crate3".to_string()));
392392
assert_eq!(dirs[4].level, LevelFilter::TRACE);
393-
assert_eq!(dirs[4].field_names, FilterVec::<String>::default());
393+
assert_eq!(dirs[4].field_names, Vec::<String>::new());
394394

395395
assert_eq!(dirs[5].target, Some("crate2".to_string()));
396396
assert_eq!(dirs[5].level, LevelFilter::DEBUG);
397-
assert_eq!(dirs[5].field_names, FilterVec::<String>::default());
397+
assert_eq!(dirs[5].field_names, Vec::<String>::new());
398398
}
399399

400400
#[test]
@@ -418,19 +418,19 @@ mod tests {
418418
assert_eq!(dirs.len(), 4, "\nparsed: {:#?}", dirs);
419419
assert_eq!(dirs[0].target, Some("crate1::mod2".to_string()));
420420
assert_eq!(dirs[0].level, LevelFilter::TRACE);
421-
assert_eq!(dirs[0].field_names, FilterVec::<String>::default());
421+
assert_eq!(dirs[0].field_names, Vec::<String>::new());
422422

423423
assert_eq!(dirs[1].target, Some("crate1::mod1".to_string()));
424424
assert_eq!(dirs[1].level, LevelFilter::ERROR);
425-
assert_eq!(dirs[1].field_names, FilterVec::<String>::default());
425+
assert_eq!(dirs[1].field_names, Vec::<String>::new());
426426

427427
assert_eq!(dirs[2].target, Some("crate3".to_string()));
428428
assert_eq!(dirs[2].level, LevelFilter::OFF);
429-
assert_eq!(dirs[2].field_names, FilterVec::<String>::default());
429+
assert_eq!(dirs[2].field_names, Vec::<String>::new());
430430

431431
assert_eq!(dirs[3].target, Some("crate2".to_string()));
432432
assert_eq!(dirs[3].level, LevelFilter::DEBUG);
433-
assert_eq!(dirs[3].field_names, FilterVec::<String>::default());
433+
assert_eq!(dirs[3].field_names, Vec::<String>::new());
434434
}
435435

436436
#[test]
@@ -456,4 +456,25 @@ mod tests {
456456
crate3=5,crate3::mod2::mod1=0",
457457
)
458458
}
459+
460+
#[test]
461+
fn size_of_filters() {
462+
fn print_sz(s: &str) {
463+
let filter = s.parse::<Targets>().expect("filter should parse");
464+
println!(
465+
"size_of_val({:?})\n -> {}B",
466+
s,
467+
std::mem::size_of_val(&filter)
468+
);
469+
}
470+
471+
print_sz("info");
472+
473+
print_sz("foo=debug");
474+
475+
print_sz(
476+
"crate1::mod1=error,crate1::mod2=warn,crate1::mod2::mod3=info,\
477+
crate2=debug,crate3=trace,crate3::mod2::mod1=off",
478+
);
479+
}
459480
}

0 commit comments

Comments
 (0)