about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2018-09-20 00:16:46 +0000
committerbors <bors@rust-lang.org>2018-09-20 00:16:46 +0000
commit1d33aedaa928cc92d58c0bfe7ff433714ff52976 (patch)
tree35653d8e90a5db7fbec04145fc91654a42b496e3
parent20dc0c50704ba1fc8c56a88ae2bf05ddb3e419bc (diff)
parentefae70c73621046d5ba33a341bfc6e4100c32945 (diff)
downloadrust-1d33aedaa928cc92d58c0bfe7ff433714ff52976.tar.gz
rust-1d33aedaa928cc92d58c0bfe7ff433714ff52976.zip
Auto merge of #54211 - nnethercote:keccak-Liveness-memory, r=nikomatsakis
Split `Liveness::users` into three.

This reduces memory usage on some benchmarks because no space is wasted
for padding. For a `check-clean` build of `keccak` it reduces `max-rss`
by 20%.

r? @nikomatsakis, but I want to do a perf run. Locally, I had these results:
- instructions: slight regression
- max-rss: big win on "Clean" builds
- faults: big win on "Clean" and "Nll" builds
- wall-time: small win on "Clean" and "Nll" builds

So I want to see how a different machine compares.
-rw-r--r--src/librustc/middle/liveness.rs74
1 files changed, 35 insertions, 39 deletions
diff --git a/src/librustc/middle/liveness.rs b/src/librustc/middle/liveness.rs
index c34a0a654e6..13847fb48ce 100644
--- a/src/librustc/middle/liveness.rs
+++ b/src/librustc/middle/liveness.rs
@@ -64,10 +64,10 @@
 //! methods.  It effectively does a reverse walk of the AST; whenever we
 //! reach a loop node, we iterate until a fixed point is reached.
 //!
-//! ## The `Users` struct
+//! ## The `users_*` fields
 //!
 //! At each live node `N`, we track three pieces of information for each
-//! variable `V` (these are encapsulated in the `Users` struct):
+//! variable `V` (these are in the `users_*` fields):
 //!
 //! - `reader`: the `LiveNode` ID of some node which will read the value
 //!    that `V` holds on entry to `N`.  Formally: a node `M` such
@@ -536,21 +536,6 @@ fn visit_expr<'a, 'tcx>(ir: &mut IrMaps<'a, 'tcx>, expr: &'tcx Expr) {
 // Actually we compute just a bit more than just liveness, but we use
 // the same basic propagation framework in all cases.
 
-#[derive(Clone, Copy)]
-struct Users {
-    reader: LiveNode,
-    writer: LiveNode,
-    used: bool
-}
-
-fn invalid_users() -> Users {
-    Users {
-        reader: invalid_node(),
-        writer: invalid_node(),
-        used: false
-    }
-}
-
 #[derive(Copy, Clone)]
 struct Specials {
     exit_ln: LiveNode,
@@ -567,7 +552,14 @@ struct Liveness<'a, 'tcx: 'a> {
     tables: &'a ty::TypeckTables<'tcx>,
     s: Specials,
     successors: Vec<LiveNode>,
-    users: Vec<Users>,
+
+    // We used to have a single `users: Vec<Users>` field here, where `Users`
+    // had `reader`, `writer` and `used` fields. But the number of users can
+    // get very large, and it's more compact to store the data in three
+    // separate `Vec`s so that no space is wasted for padding.
+    users_reader: Vec<LiveNode>,
+    users_writer: Vec<LiveNode>,
+    users_used: Vec<bool>,
 
     // mappings from loop node ID to LiveNode
     // ("break" label should map to loop node ID,
@@ -592,13 +584,16 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
 
         let num_live_nodes = ir.num_live_nodes;
         let num_vars = ir.num_vars;
+        let num_users = num_live_nodes * num_vars;
 
         Liveness {
             ir,
             tables,
             s: specials,
             successors: vec![invalid_node(); num_live_nodes],
-            users: vec![invalid_users(); num_live_nodes * num_vars],
+            users_reader: vec![invalid_node(); num_users],
+            users_writer: vec![invalid_node(); num_users],
+            users_used: vec![false; num_users],
             break_ln: NodeMap(),
             cont_ln: NodeMap(),
         }
@@ -665,7 +660,7 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
     fn live_on_entry(&self, ln: LiveNode, var: Variable)
                       -> Option<LiveNodeKind> {
         assert!(ln.is_valid());
-        let reader = self.users[self.idx(ln, var)].reader;
+        let reader = self.users_reader[self.idx(ln, var)];
         if reader.is_valid() {Some(self.ir.lnk(reader))} else {None}
     }
 
@@ -680,13 +675,13 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
 
     fn used_on_entry(&self, ln: LiveNode, var: Variable) -> bool {
         assert!(ln.is_valid());
-        self.users[self.idx(ln, var)].used
+        self.users_used[self.idx(ln, var)]
     }
 
     fn assigned_on_entry(&self, ln: LiveNode, var: Variable)
                          -> Option<LiveNodeKind> {
         assert!(ln.is_valid());
-        let writer = self.users[self.idx(ln, var)].writer;
+        let writer = self.users_writer[self.idx(ln, var)];
         if writer.is_valid() {Some(self.ir.lnk(writer))} else {None}
     }
 
@@ -730,9 +725,9 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
         {
             let wr = &mut wr as &mut dyn Write;
             write!(wr, "[ln({:?}) of kind {:?} reads", ln.get(), self.ir.lnk(ln));
-            self.write_vars(wr, ln, |idx| self.users[idx].reader);
+            self.write_vars(wr, ln, |idx| self.users_reader[idx]);
             write!(wr, "  writes");
-            self.write_vars(wr, ln, |idx| self.users[idx].writer);
+            self.write_vars(wr, ln, |idx| self.users_writer[idx]);
             write!(wr, "  precedes {:?}]", self.successors[ln.get()]);
         }
         String::from_utf8(wr).unwrap()
@@ -747,7 +742,9 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
         // only grow during iterations.
         //
         // self.indices(ln) { |idx|
-        //     self.users[idx] = invalid_users();
+        //     self.users_reader[idx] = invalid_node();
+        //     self.users_writer[idx] = invalid_node();
+        //     self.users_used[idx] = false;
         // }
     }
 
@@ -756,7 +753,9 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
         self.successors[ln.get()] = succ_ln;
 
         self.indices2(ln, succ_ln, |this, idx, succ_idx| {
-            this.users[idx] = this.users[succ_idx]
+            this.users_reader[idx] = this.users_reader[succ_idx];
+            this.users_writer[idx] = this.users_writer[succ_idx];
+            this.users_used[idx] = this.users_used[succ_idx];
         });
         debug!("init_from_succ(ln={}, succ={})",
                self.ln_str(ln), self.ln_str(succ_ln));
@@ -771,12 +770,10 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
 
         let mut changed = false;
         self.indices2(ln, succ_ln, |this, idx, succ_idx| {
-            changed |= copy_if_invalid(this.users[succ_idx].reader,
-                                       &mut this.users[idx].reader);
-            changed |= copy_if_invalid(this.users[succ_idx].writer,
-                                       &mut this.users[idx].writer);
-            if this.users[succ_idx].used && !this.users[idx].used {
-                this.users[idx].used = true;
+            changed |= copy_if_invalid(this.users_reader[succ_idx], &mut this.users_reader[idx]);
+            changed |= copy_if_invalid(this.users_writer[succ_idx], &mut this.users_writer[idx]);
+            if this.users_used[succ_idx] && !this.users_used[idx] {
+                this.users_used[idx] = true;
                 changed = true;
             }
         });
@@ -800,8 +797,8 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
     // this) so we just clear out all the data.
     fn define(&mut self, writer: LiveNode, var: Variable) {
         let idx = self.idx(writer, var);
-        self.users[idx].reader = invalid_node();
-        self.users[idx].writer = invalid_node();
+        self.users_reader[idx] = invalid_node();
+        self.users_writer[idx] = invalid_node();
 
         debug!("{:?} defines {:?} (idx={}): {}", writer, var,
                idx, self.ln_str(writer));
@@ -813,21 +810,20 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
                ln, acc, var, self.ln_str(ln));
 
         let idx = self.idx(ln, var);
-        let user = &mut self.users[idx];
 
         if (acc & ACC_WRITE) != 0 {
-            user.reader = invalid_node();
-            user.writer = ln;
+            self.users_reader[idx] = invalid_node();
+            self.users_writer[idx] = ln;
         }
 
         // Important: if we both read/write, must do read second
         // or else the write will override.
         if (acc & ACC_READ) != 0 {
-            user.reader = ln;
+            self.users_reader[idx] = ln;
         }
 
         if (acc & ACC_USE) != 0 {
-            user.used = true;
+            self.users_used[idx] = true;
         }
     }