Skip to content

Commit f40f04b

Browse files
authored
Rollup merge of #55932 - Turbo87:to_digit, r=alexcrichton
core/char: Speed up `to_digit()` for `radix <= 10` I noticed that `char::to_digit()` seemed to do a bit of extra work for handling `[a-zA-Z]` characters. Since `to_digit(10)` seems to be the most common case (at least in the `rust` codebase) I thought it might be valuable to create a fast path for that case, and according to the benchmarks that I added in one of the commits it seems to pay off. I also created another fast path for the `radix < 10` case, which also seems to have a positive effect. It is very well possible that I'm measuring something entirely unrelated though, so please verify these numbers and let me know if I missed something! ### Before ``` # Run 1 test char::methods::bench_to_digit_radix_10 ... bench: 16,265 ns/iter (+/- 1,774) test char::methods::bench_to_digit_radix_16 ... bench: 13,938 ns/iter (+/- 2,479) test char::methods::bench_to_digit_radix_2 ... bench: 13,090 ns/iter (+/- 524) test char::methods::bench_to_digit_radix_36 ... bench: 14,236 ns/iter (+/- 1,949) # Run 2 test char::methods::bench_to_digit_radix_10 ... bench: 16,176 ns/iter (+/- 1,589) test char::methods::bench_to_digit_radix_16 ... bench: 13,896 ns/iter (+/- 3,140) test char::methods::bench_to_digit_radix_2 ... bench: 13,158 ns/iter (+/- 1,112) test char::methods::bench_to_digit_radix_36 ... bench: 14,206 ns/iter (+/- 1,312) # Run 3 test char::methods::bench_to_digit_radix_10 ... bench: 16,221 ns/iter (+/- 2,423) test char::methods::bench_to_digit_radix_16 ... bench: 14,361 ns/iter (+/- 3,926) test char::methods::bench_to_digit_radix_2 ... bench: 13,097 ns/iter (+/- 671) test char::methods::bench_to_digit_radix_36 ... bench: 14,388 ns/iter (+/- 1,068) ``` ### After ``` # Run 1 test char::methods::bench_to_digit_radix_10 ... bench: 11,521 ns/iter (+/- 552) test char::methods::bench_to_digit_radix_16 ... bench: 12,926 ns/iter (+/- 684) test char::methods::bench_to_digit_radix_2 ... bench: 11,266 ns/iter (+/- 1,085) test char::methods::bench_to_digit_radix_36 ... bench: 14,213 ns/iter (+/- 614) # Run 2 test char::methods::bench_to_digit_radix_10 ... bench: 11,424 ns/iter (+/- 1,042) test char::methods::bench_to_digit_radix_16 ... bench: 12,854 ns/iter (+/- 1,193) test char::methods::bench_to_digit_radix_2 ... bench: 11,193 ns/iter (+/- 716) test char::methods::bench_to_digit_radix_36 ... bench: 14,249 ns/iter (+/- 3,514) # Run 3 test char::methods::bench_to_digit_radix_10 ... bench: 11,469 ns/iter (+/- 685) test char::methods::bench_to_digit_radix_16 ... bench: 12,852 ns/iter (+/- 568) test char::methods::bench_to_digit_radix_2 ... bench: 11,275 ns/iter (+/- 1,356) test char::methods::bench_to_digit_radix_36 ... bench: 14,188 ns/iter (+/- 1,501) ``` I ran the benchmark using: ```sh python x.py bench src/libcore --stage 1 --keep-stage 0 --test-args "bench_to_digit" ```
2 parents c915f92 + 7843e27 commit f40f04b

File tree

4 files changed

+71
-8
lines changed

4 files changed

+71
-8
lines changed

Diff for: src/libcore/benches/char/methods.rs

+42
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
use test::Bencher;
12+
13+
const CHARS: [char; 9] = ['0', 'x', '2', '5', 'A', 'f', '7', '8', '9'];
14+
const RADIX: [u32; 5] = [2, 8, 10, 16, 32];
15+
16+
#[bench]
17+
fn bench_to_digit_radix_2(b: &mut Bencher) {
18+
b.iter(|| CHARS.iter().cycle().take(10_000).map(|c| c.to_digit(2)).min())
19+
}
20+
21+
#[bench]
22+
fn bench_to_digit_radix_10(b: &mut Bencher) {
23+
b.iter(|| CHARS.iter().cycle().take(10_000).map(|c| c.to_digit(10)).min())
24+
}
25+
26+
#[bench]
27+
fn bench_to_digit_radix_16(b: &mut Bencher) {
28+
b.iter(|| CHARS.iter().cycle().take(10_000).map(|c| c.to_digit(16)).min())
29+
}
30+
31+
#[bench]
32+
fn bench_to_digit_radix_36(b: &mut Bencher) {
33+
b.iter(|| CHARS.iter().cycle().take(10_000).map(|c| c.to_digit(36)).min())
34+
}
35+
36+
#[bench]
37+
fn bench_to_digit_radix_var(b: &mut Bencher) {
38+
b.iter(|| CHARS.iter().cycle()
39+
.zip(RADIX.iter().cycle())
40+
.take(10_000)
41+
.map(|(c, radix)| c.to_digit(*radix)).min())
42+
}

Diff for: src/libcore/benches/char/mod.rs

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
mod methods;

Diff for: src/libcore/benches/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ extern crate core;
1515
extern crate test;
1616

1717
mod any;
18+
mod char;
1819
mod hash;
1920
mod iter;
2021
mod num;

Diff for: src/libcore/char/methods.rs

+17-8
Original file line numberDiff line numberDiff line change
@@ -121,15 +121,24 @@ impl char {
121121
#[stable(feature = "rust1", since = "1.0.0")]
122122
#[inline]
123123
pub fn to_digit(self, radix: u32) -> Option<u32> {
124-
if radix > 36 {
125-
panic!("to_digit: radix is too high (maximum 36)");
126-
}
127-
let val = match self {
128-
'0' ..= '9' => self as u32 - '0' as u32,
129-
'a' ..= 'z' => self as u32 - 'a' as u32 + 10,
130-
'A' ..= 'Z' => self as u32 - 'A' as u32 + 10,
131-
_ => return None,
124+
assert!(radix <= 36, "to_digit: radix is too high (maximum 36)");
125+
126+
// the code is split up here to improve execution speed for cases where
127+
// the `radix` is constant and 10 or smaller
128+
let val = if radix <= 10 {
129+
match self {
130+
'0' ..= '9' => self as u32 - '0' as u32,
131+
_ => return None,
132+
}
133+
} else {
134+
match self {
135+
'0'..='9' => self as u32 - '0' as u32,
136+
'a'..='z' => self as u32 - 'a' as u32 + 10,
137+
'A'..='Z' => self as u32 - 'A' as u32 + 10,
138+
_ => return None,
139+
}
132140
};
141+
133142
if val < radix { Some(val) }
134143
else { None }
135144
}

0 commit comments

Comments
 (0)