refactor: add fast branch for ascii-only string truncate (#1330)

This is just a first attempt to speed up what looked like a hot spot in samply's profiling results.

Benchmark code and results here: https://gist.github.com/ClementTsang/e242f12f7e1d1902ed414dcc18c3b321
This commit is contained in:
Clement Tsang 2023-11-24 04:45:03 +00:00 committed by GitHub
parent 3a50d7622e
commit a93521d2b1
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 78 additions and 36 deletions

3
.gitignore vendored
View file

@ -42,3 +42,6 @@ dhat/
# cargo vet
supply-chain/
# samply profiling
profile.json

View file

@ -100,9 +100,23 @@ fn grapheme_width(g: &str) -> usize {
#[inline]
fn truncate_str<U: Into<usize>>(content: &str, width: U) -> String {
let width = width.into();
let mut text = String::with_capacity(width);
if width > 0 {
if content.is_ascii() {
// If the entire string is ASCII, we can use a much simpler approach.
if content.len() <= width {
content.to_owned()
} else {
let mut text = String::with_capacity(width);
let (keep, _throw) = content.split_at(width - 1);
text.push_str(keep);
text.push('…');
text
}
} else {
let mut text = String::with_capacity(width);
let mut curr_width = 0;
let mut early_break = false;
@ -134,10 +148,12 @@ fn truncate_str<U: Into<usize>>(content: &str, width: U) -> String {
}
text.push('…');
}
}
text
}
} else {
String::default()
}
}
#[inline]
pub const fn sort_partial_fn<T: PartialOrd>(is_descending: bool) -> fn(T, T) -> Ordering {
@ -252,7 +268,7 @@ mod test {
assert_eq!(
truncate_str(cpu_header, 8_usize),
cpu_header,
"should match base string as there is enough room"
"should match base string as there is extra room"
);
assert_eq!(
@ -269,13 +285,36 @@ mod test {
}
#[test]
fn truncate_cjk() {
fn test_truncate_ascii() {
let content = "0123456";
assert_eq!(
truncate_str(content, 8_usize),
content,
"should match base string as there is extra room"
);
assert_eq!(
truncate_str(content, 7_usize),
content,
"should match base string as there is enough room"
);
assert_eq!(truncate_str(content, 6_usize), "01234…");
assert_eq!(truncate_str(content, 5_usize), "0123…");
assert_eq!(truncate_str(content, 4_usize), "012…");
assert_eq!(truncate_str(content, 1_usize), "");
assert_eq!(truncate_str(content, 0_usize), "");
}
#[test]
fn test_truncate_cjk() {
let cjk = "施氏食獅史";
assert_eq!(
truncate_str(cjk, 11_usize),
cjk,
"should match base string as there is enough room"
"should match base string as there is extra room"
);
assert_eq!(
@ -292,13 +331,13 @@ mod test {
}
#[test]
fn truncate_mixed() {
fn test_truncate_mixed() {
let test = "Test (施氏食獅史) Test";
assert_eq!(
truncate_str(test, 30_usize),
test,
"should match base string as there is enough room"
"should match base string as there is extra room"
);
assert_eq!(
@ -325,7 +364,7 @@ mod test {
}
#[test]
fn truncate_flags() {
fn test_truncate_flags() {
let flag = "🇨🇦";
assert_eq!(truncate_str(flag, 3_usize), flag);
assert_eq!(truncate_str(flag, 2_usize), flag);
@ -368,7 +407,7 @@ mod test {
/// This might not be the best way to handle it, but this at least tests that it doesn't crash...
#[test]
fn truncate_hindi() {
fn test_truncate_hindi() {
// cSpell:disable
let test = "हिन्दी";
assert_eq!(truncate_str(test, 10_usize), test);