]> git.lizzy.rs Git - rust.git/commitdiff
rollup merge of #20079: SimonSapin/string_push_ascii_fast_path
authorAlex Crichton <alex@alexcrichton.com>
Sun, 21 Dec 2014 08:04:26 +0000 (00:04 -0800)
committerAlex Crichton <alex@alexcrichton.com>
Sun, 21 Dec 2014 17:27:37 +0000 (09:27 -0800)
`String::push(&mut self, ch: char)` currently has a single code path that calls `Char::encode_utf8`. This adds a fast path for ASCII `char`s, which are represented as a single byte in UTF-8.

Benchmarks of stage1 libcollections at the intermediate commit show that the fast path very significantly improves the performance of repeatedly pushing an ASCII `char`, but does not significantly affect the performance for a non-ASCII `char` (where the fast path is not taken).

```
bench_push_char_one_byte                  59552 ns/iter (+/- 2132) = 167 MB/s
bench_push_char_one_byte_with_fast_path    6563 ns/iter (+/- 658) = 1523 MB/s
bench_push_char_two_bytes                 71520 ns/iter (+/- 3541) = 279 MB/s
bench_push_char_two_bytes_with_slow_path  71452 ns/iter (+/- 4202) = 279 MB/s
bench_push_str_one_byte                   38910 ns/iter (+/- 2477) = 257 MB/s
```

A benchmark of pushing a one-byte-long `&str` is added for comparison, but its performance [has varied a lot lately](https://github.com/rust-lang/rust/pull/19640#issuecomment-67741561). (When the input is fixed, `s.push_str("x")` could be used just as well as `s.push('x')`.)

1  2 
src/libcollections/string.rs

index 3837dd703beaeff7b9464fcf929590675ef52916,678e81d40b4a5cb76881d1265dbcdc198945b656..76f69dbdb744bdd2c322fb461e7a4cff3023be4c
@@@ -26,8 -26,7 +26,8 @@@ use core::raw::Slice as RawSlice
  
  use slice::CloneSliceExt;
  use str;
 -use str::{CharRange, CowString, FromStr, StrAllocating, Owned};
 +use str::{CharRange, CowString, FromStr, StrAllocating};
 +use str::MaybeOwned::Owned;
  use vec::{DerefVec, Vec, as_vec};
  
  /// A growable string stored as a UTF-8 encoded buffer.
@@@ -513,6 -512,11 +513,11 @@@ impl String 
      #[inline]
      #[stable = "function just renamed from push_char"]
      pub fn push(&mut self, ch: char) {
+         if (ch as u32) < 0x80 {
+             self.vec.push(ch as u8);
+             return;
+         }
          let cur_len = self.len();
          // This may use up to 4 bytes.
          self.vec.reserve(4);
@@@ -886,6 -890,14 +891,6 @@@ impl<'a> Add<&'a str, String> for Strin
      }
  }
  
 -#[cfg(not(stage0))]  // NOTE(stage0): Remove cfg after a snapshot
 -impl<'a> Add<String, String> for &'a str {
 -    fn add(self, mut other: String) -> String {
 -        other.push_str(self);
 -        other
 -    }
 -}
 -
  impl ops::Slice<uint, str> for String {
      #[inline]
      fn as_slice_<'a>(&'a self) -> &'a str {
@@@ -1401,6 -1413,41 +1406,41 @@@ mod tests 
          });
      }
  
+     const REPETITIONS: u64 = 10_000;
+     #[bench]
+     fn bench_push_str_one_byte(b: &mut Bencher) {
+         b.bytes = REPETITIONS;
+         b.iter(|| {
+             let mut r = String::new();
+             for _ in range(0, REPETITIONS) {
+                 r.push_str("a")
+             }
+         });
+     }
+     #[bench]
+     fn bench_push_char_one_byte(b: &mut Bencher) {
+         b.bytes = REPETITIONS;
+         b.iter(|| {
+             let mut r = String::new();
+             for _ in range(0, REPETITIONS) {
+                 r.push('a')
+             }
+         });
+     }
+     #[bench]
+     fn bench_push_char_two_bytes(b: &mut Bencher) {
+         b.bytes = REPETITIONS * 2;
+         b.iter(|| {
+             let mut r = String::new();
+             for _ in range(0, REPETITIONS) {
+                 r.push('รข')
+             }
+         });
+     }
      #[bench]
      fn from_utf8_lossy_100_ascii(b: &mut Bencher) {
          let s = b"Hello there, the quick brown fox jumped over the lazy dog! \