Skip to content

Commit a6ad0ce

Browse files
committed
perf: stats cache repetitive expensive env_var access in hot path
1 parent f141c1b commit a6ad0ce

File tree

1 file changed

+21
-15
lines changed

1 file changed

+21
-15
lines changed

src/cmd/stats.rs

Lines changed: 21 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -578,6 +578,8 @@ pub static STATSDATA_TYPES_MAP: phf::Map<&'static str, JsonTypes> = phf_map! {
578578
static INFER_DATE_FLAGS: OnceLock<SmallVec<[bool; 50]>> = OnceLock::new();
579579
static RECORD_COUNT: OnceLock<u64> = OnceLock::new();
580580
static ANTIMODES_LEN: OnceLock<usize> = OnceLock::new();
581+
static STATS_SEPARATOR: OnceLock<String> = OnceLock::new();
582+
static STATS_STRING_MAX_LENGTH: OnceLock<Option<usize>> = OnceLock::new();
581583

582584
// standard overflow and underflow strings
583585
// for sum, sum_length and avg_length
@@ -2400,12 +2402,14 @@ impl Stats {
24002402
let record_count = *RECORD_COUNT.get().unwrap_or(&1);
24012403

24022404
// get the stats separator
2403-
let stats_separator = if self.which.mode || self.which.percentiles {
2404-
std::env::var("QSV_STATS_SEPARATOR")
2405-
.unwrap_or_else(|_| DEFAULT_STATS_SEPARATOR.to_string())
2406-
} else {
2407-
DEFAULT_STATS_SEPARATOR.to_string()
2408-
};
2405+
let stats_separator = STATS_SEPARATOR.get_or_init(|| {
2406+
if self.which.mode || self.which.percentiles {
2407+
std::env::var("QSV_STATS_SEPARATOR")
2408+
.unwrap_or_else(|_| DEFAULT_STATS_SEPARATOR.to_string())
2409+
} else {
2410+
DEFAULT_STATS_SEPARATOR.to_string()
2411+
}
2412+
});
24092413

24102414
// modes/antimodes & cardinality/uniqueness_ratio
24112415
// we do this second because we can use the sort order with cardinality, to skip sorting
@@ -2466,12 +2470,12 @@ impl Stats {
24662470
modes_result
24672471
.iter()
24682472
.map(|c| util::visualize_whitespace(&String::from_utf8_lossy(c)))
2469-
.join(&stats_separator)
2473+
.join(stats_separator)
24702474
} else {
24712475
modes_result
24722476
.iter()
24732477
.map(|c| String::from_utf8_lossy(c))
2474-
.join(&stats_separator)
2478+
.join(stats_separator)
24752479
};
24762480

24772481
// antimode/s ============
@@ -2497,11 +2501,11 @@ impl Stats {
24972501
let antimodes_vals = &antimodes_result
24982502
.iter()
24992503
.map(|c| String::from_utf8_lossy(c))
2500-
.join(&stats_separator);
2504+
.join(stats_separator);
25012505

25022506
// if the antimodes result starts with the separator,
25032507
// it indicates that NULL is the first antimode. Add NULL to the list.
2504-
if antimodes_vals.starts_with(&stats_separator) {
2508+
if antimodes_vals.starts_with(stats_separator) {
25052509
antimodes_list.push_str("NULL");
25062510
}
25072511
antimodes_list.push_str(antimodes_vals);
@@ -2914,7 +2918,7 @@ impl Stats {
29142918
.map(|p| util::round_num(*p, round_places))
29152919
.collect::<Vec<_>>()
29162920
};
2917-
pieces.push(formatted_values.join(&stats_separator));
2921+
pieces.push(formatted_values.join(stats_separator));
29182922
} else {
29192923
pieces.push(empty());
29202924
}
@@ -3249,11 +3253,13 @@ impl TypedMinMax {
32493253
let min_str = String::from_utf8_lossy(min).to_string();
32503254
let max_str = String::from_utf8_lossy(max).to_string();
32513255

3252-
let max_length = std::env::var("QSV_STATS_STRING_MAX_LENGTH")
3253-
.ok()
3254-
.and_then(|s| atoi_simd::parse::<usize>(s.as_bytes()).ok());
3256+
let max_length = STATS_STRING_MAX_LENGTH.get_or_init(|| {
3257+
std::env::var("QSV_STATS_STRING_MAX_LENGTH")
3258+
.ok()
3259+
.and_then(|s| atoi_simd::parse::<usize>(s.as_bytes()).ok())
3260+
});
32553261

3256-
let (min_str, max_str) = if let Some(max_len) = max_length {
3262+
let (min_str, max_str) = if let Some(max_len) = *max_length {
32573263
(
32583264
if min_str.len() > max_len {
32593265
format!("{}...", &min_str[..max_len])

0 commit comments

Comments
 (0)