Skip to content

Commit 1d33aed

Browse files
committed
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.
2 parents 20dc0c5 + efae70c commit 1d33aed

File tree

1 file changed

+35
-39
lines changed

1 file changed

+35
-39
lines changed

src/librustc/middle/liveness.rs

+35-39
Original file line numberDiff line numberDiff line change
@@ -64,10 +64,10 @@
6464
//! methods. It effectively does a reverse walk of the AST; whenever we
6565
//! reach a loop node, we iterate until a fixed point is reached.
6666
//!
67-
//! ## The `Users` struct
67+
//! ## The `users_*` fields
6868
//!
6969
//! At each live node `N`, we track three pieces of information for each
70-
//! variable `V` (these are encapsulated in the `Users` struct):
70+
//! variable `V` (these are in the `users_*` fields):
7171
//!
7272
//! - `reader`: the `LiveNode` ID of some node which will read the value
7373
//! 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) {
536536
// Actually we compute just a bit more than just liveness, but we use
537537
// the same basic propagation framework in all cases.
538538

539-
#[derive(Clone, Copy)]
540-
struct Users {
541-
reader: LiveNode,
542-
writer: LiveNode,
543-
used: bool
544-
}
545-
546-
fn invalid_users() -> Users {
547-
Users {
548-
reader: invalid_node(),
549-
writer: invalid_node(),
550-
used: false
551-
}
552-
}
553-
554539
#[derive(Copy, Clone)]
555540
struct Specials {
556541
exit_ln: LiveNode,
@@ -567,7 +552,14 @@ struct Liveness<'a, 'tcx: 'a> {
567552
tables: &'a ty::TypeckTables<'tcx>,
568553
s: Specials,
569554
successors: Vec<LiveNode>,
570-
users: Vec<Users>,
555+
556+
// We used to have a single `users: Vec<Users>` field here, where `Users`
557+
// had `reader`, `writer` and `used` fields. But the number of users can
558+
// get very large, and it's more compact to store the data in three
559+
// separate `Vec`s so that no space is wasted for padding.
560+
users_reader: Vec<LiveNode>,
561+
users_writer: Vec<LiveNode>,
562+
users_used: Vec<bool>,
571563

572564
// mappings from loop node ID to LiveNode
573565
// ("break" label should map to loop node ID,
@@ -592,13 +584,16 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
592584

593585
let num_live_nodes = ir.num_live_nodes;
594586
let num_vars = ir.num_vars;
587+
let num_users = num_live_nodes * num_vars;
595588

596589
Liveness {
597590
ir,
598591
tables,
599592
s: specials,
600593
successors: vec![invalid_node(); num_live_nodes],
601-
users: vec![invalid_users(); num_live_nodes * num_vars],
594+
users_reader: vec![invalid_node(); num_users],
595+
users_writer: vec![invalid_node(); num_users],
596+
users_used: vec![false; num_users],
602597
break_ln: NodeMap(),
603598
cont_ln: NodeMap(),
604599
}
@@ -665,7 +660,7 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
665660
fn live_on_entry(&self, ln: LiveNode, var: Variable)
666661
-> Option<LiveNodeKind> {
667662
assert!(ln.is_valid());
668-
let reader = self.users[self.idx(ln, var)].reader;
663+
let reader = self.users_reader[self.idx(ln, var)];
669664
if reader.is_valid() {Some(self.ir.lnk(reader))} else {None}
670665
}
671666

@@ -680,13 +675,13 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
680675

681676
fn used_on_entry(&self, ln: LiveNode, var: Variable) -> bool {
682677
assert!(ln.is_valid());
683-
self.users[self.idx(ln, var)].used
678+
self.users_used[self.idx(ln, var)]
684679
}
685680

686681
fn assigned_on_entry(&self, ln: LiveNode, var: Variable)
687682
-> Option<LiveNodeKind> {
688683
assert!(ln.is_valid());
689-
let writer = self.users[self.idx(ln, var)].writer;
684+
let writer = self.users_writer[self.idx(ln, var)];
690685
if writer.is_valid() {Some(self.ir.lnk(writer))} else {None}
691686
}
692687

@@ -730,9 +725,9 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
730725
{
731726
let wr = &mut wr as &mut dyn Write;
732727
write!(wr, "[ln({:?}) of kind {:?} reads", ln.get(), self.ir.lnk(ln));
733-
self.write_vars(wr, ln, |idx| self.users[idx].reader);
728+
self.write_vars(wr, ln, |idx| self.users_reader[idx]);
734729
write!(wr, " writes");
735-
self.write_vars(wr, ln, |idx| self.users[idx].writer);
730+
self.write_vars(wr, ln, |idx| self.users_writer[idx]);
736731
write!(wr, " precedes {:?}]", self.successors[ln.get()]);
737732
}
738733
String::from_utf8(wr).unwrap()
@@ -747,7 +742,9 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
747742
// only grow during iterations.
748743
//
749744
// self.indices(ln) { |idx|
750-
// self.users[idx] = invalid_users();
745+
// self.users_reader[idx] = invalid_node();
746+
// self.users_writer[idx] = invalid_node();
747+
// self.users_used[idx] = false;
751748
// }
752749
}
753750

@@ -756,7 +753,9 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
756753
self.successors[ln.get()] = succ_ln;
757754

758755
self.indices2(ln, succ_ln, |this, idx, succ_idx| {
759-
this.users[idx] = this.users[succ_idx]
756+
this.users_reader[idx] = this.users_reader[succ_idx];
757+
this.users_writer[idx] = this.users_writer[succ_idx];
758+
this.users_used[idx] = this.users_used[succ_idx];
760759
});
761760
debug!("init_from_succ(ln={}, succ={})",
762761
self.ln_str(ln), self.ln_str(succ_ln));
@@ -771,12 +770,10 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
771770

772771
let mut changed = false;
773772
self.indices2(ln, succ_ln, |this, idx, succ_idx| {
774-
changed |= copy_if_invalid(this.users[succ_idx].reader,
775-
&mut this.users[idx].reader);
776-
changed |= copy_if_invalid(this.users[succ_idx].writer,
777-
&mut this.users[idx].writer);
778-
if this.users[succ_idx].used && !this.users[idx].used {
779-
this.users[idx].used = true;
773+
changed |= copy_if_invalid(this.users_reader[succ_idx], &mut this.users_reader[idx]);
774+
changed |= copy_if_invalid(this.users_writer[succ_idx], &mut this.users_writer[idx]);
775+
if this.users_used[succ_idx] && !this.users_used[idx] {
776+
this.users_used[idx] = true;
780777
changed = true;
781778
}
782779
});
@@ -800,8 +797,8 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
800797
// this) so we just clear out all the data.
801798
fn define(&mut self, writer: LiveNode, var: Variable) {
802799
let idx = self.idx(writer, var);
803-
self.users[idx].reader = invalid_node();
804-
self.users[idx].writer = invalid_node();
800+
self.users_reader[idx] = invalid_node();
801+
self.users_writer[idx] = invalid_node();
805802

806803
debug!("{:?} defines {:?} (idx={}): {}", writer, var,
807804
idx, self.ln_str(writer));
@@ -813,21 +810,20 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
813810
ln, acc, var, self.ln_str(ln));
814811

815812
let idx = self.idx(ln, var);
816-
let user = &mut self.users[idx];
817813

818814
if (acc & ACC_WRITE) != 0 {
819-
user.reader = invalid_node();
820-
user.writer = ln;
815+
self.users_reader[idx] = invalid_node();
816+
self.users_writer[idx] = ln;
821817
}
822818

823819
// Important: if we both read/write, must do read second
824820
// or else the write will override.
825821
if (acc & ACC_READ) != 0 {
826-
user.reader = ln;
822+
self.users_reader[idx] = ln;
827823
}
828824

829825
if (acc & ACC_USE) != 0 {
830-
user.used = true;
826+
self.users_used[idx] = true;
831827
}
832828
}
833829

0 commit comments

Comments
 (0)