]> git.lizzy.rs Git - rust.git/commitdiff
Use `SmallVec` for the queue in `coerce_unsized`.
authorNicholas Nethercote <nnethercote@mozilla.com>
Fri, 26 Oct 2018 08:33:51 +0000 (19:33 +1100)
committerNicholas Nethercote <nnethercote@mozilla.com>
Fri, 26 Oct 2018 08:33:51 +0000 (19:33 +1100)
This reduces the number of allocations done for the `tuple-stress`
benchmark by 4%.

src/librustc_typeck/check/coercion.rs

index 967c710ac34a1ae9392f799db16169e673820c5e..3bdd038bff19c2560f81b3d64ce9368a5db95b76 100644 (file)
@@ -61,7 +61,7 @@
 //! we may want to adjust precisely when coercions occur.
 
 use check::{FnCtxt, Needs};
-
+use errors::DiagnosticBuilder;
 use rustc::hir;
 use rustc::hir::def_id::DefId;
 use rustc::infer::{Coercion, InferResult, InferOk};
 use rustc::ty::fold::TypeFoldable;
 use rustc::ty::error::TypeError;
 use rustc::ty::relate::RelateResult;
-use errors::DiagnosticBuilder;
+use smallvec::{smallvec, SmallVec};
+use std::ops::Deref;
 use syntax::feature_gate;
 use syntax::ptr::P;
 use syntax_pos;
 
-use std::collections::VecDeque;
-use std::ops::Deref;
-
 struct Coerce<'a, 'gcx: 'a + 'tcx, 'tcx: 'a> {
     fcx: &'a FnCtxt<'a, 'gcx, 'tcx>,
     cause: ObligationCause<'tcx>,
@@ -536,18 +534,23 @@ fn coerce_unsized(&self, source: Ty<'tcx>, target: Ty<'tcx>) -> CoerceResult<'tc
 
         let mut selcx = traits::SelectionContext::new(self);
 
-        // Use a FIFO queue for this custom fulfillment procedure. (The maximum
-        // length is almost always 1.)
-        let mut queue = VecDeque::with_capacity(1);
-
         // Create an obligation for `Source: CoerceUnsized<Target>`.
         let cause = ObligationCause::misc(self.cause.span, self.body_id);
-        queue.push_back(self.tcx.predicate_for_trait_def(self.fcx.param_env,
-                                                         cause,
-                                                         coerce_unsized_did,
-                                                         0,
-                                                         coerce_source,
-                                                         &[coerce_target.into()]));
+
+        // Use a FIFO queue for this custom fulfillment procedure.
+        //
+        // A Vec (or SmallVec) is not a natural choice for a queue. However,
+        // this code path is hot, and this queue usually has a max length of 1
+        // and almost never more than 3. By using a SmallVec we avoid an
+        // allocation, at the (very small) cost of (occasionally) having to
+        // shift subsequent elements down when removing the front element.
+        let mut queue: SmallVec<[_; 4]> =
+            smallvec![self.tcx.predicate_for_trait_def(self.fcx.param_env,
+                                                       cause,
+                                                       coerce_unsized_did,
+                                                       0,
+                                                       coerce_source,
+                                                       &[coerce_target.into()])];
 
         let mut has_unsized_tuple_coercion = false;
 
@@ -555,7 +558,8 @@ fn coerce_unsized(&self, source: Ty<'tcx>, target: Ty<'tcx>) -> CoerceResult<'tc
         // emitting a coercion in cases like `Foo<$1>` -> `Foo<$2>`, where
         // inference might unify those two inner type variables later.
         let traits = [coerce_unsized_did, unsize_did];
-        while let Some(obligation) = queue.pop_front() {
+        while !queue.is_empty() {
+            let obligation = queue.remove(0);
             debug!("coerce_unsized resolve step: {:?}", obligation);
             let trait_ref = match obligation.predicate {
                 ty::Predicate::Trait(ref tr) if traits.contains(&tr.def_id()) => {