Skip to content

Commit 33fb63c

Browse files
committed
Lower Fingerprint alignment to reduce memory consumption
1 parent 981346f commit 33fb63c

File tree

5 files changed

+156
-56
lines changed

5 files changed

+156
-56
lines changed

compiler/rustc_ast/src/crate_disambiguator.rs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,7 @@ impl CrateDisambiguator {
2020

2121
impl fmt::Display for CrateDisambiguator {
2222
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
23-
let (a, b) = self.0.as_value();
24-
let as_u128 = a as u128 | ((b as u128) << 64);
25-
f.write_str(&base_n::encode(as_u128, base_n::CASE_INSENSITIVE))
23+
f.write_str(&base_n::encode(self.0.as_value_u128(), base_n::CASE_INSENSITIVE))
2624
}
2725
}
2826

compiler/rustc_data_structures/src/fingerprint.rs

Lines changed: 113 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -3,77 +3,152 @@ use rustc_serialize::{
33
opaque::{self, EncodeResult},
44
Decodable, Encodable,
55
};
6+
use std::cmp::Ordering;
67
use std::hash::{Hash, Hasher};
7-
use std::mem;
88

9-
#[derive(Eq, PartialEq, Ord, PartialOrd, Debug, Clone, Copy)]
10-
pub struct Fingerprint(u64, u64);
9+
#[cfg(test)]
10+
mod tests;
11+
12+
// Use `[u8; 16]` representation since it imposes no alignment requirements.
13+
// This can reduce memory consumption by preventing otherwise unnecessary
14+
// padding in arrays of structs containing `Fingerprint`s. An example of this is
15+
// the query dependency graph, which contains a large array of `DepNode`s. As of
16+
// this writing, the size of a `DepNode` decreases by ~30% (from 24 bytes to 17)
17+
// by using a byte array here instead of two `u64`s, which noticeably decreases
18+
// total memory usage when compiling large crates. However, it has the potential
19+
// to increase the instruction count slightly if not carefully implemented.
20+
#[derive(Eq, Debug, Clone, Copy)]
21+
pub struct Fingerprint([u8; 16]);
1122

1223
impl Fingerprint {
13-
pub const ZERO: Fingerprint = Fingerprint(0, 0);
24+
pub const ZERO: Fingerprint = Fingerprint([0; 16]);
1425

1526
#[inline]
16-
pub fn from_smaller_hash(hash: u64) -> Fingerprint {
17-
Fingerprint(hash, hash)
27+
fn from_value(v0: u64, v1: u64) -> Fingerprint {
28+
Fingerprint([
29+
(v0 >> 0) as u8,
30+
(v0 >> 8) as u8,
31+
(v0 >> 16) as u8,
32+
(v0 >> 24) as u8,
33+
(v0 >> 32) as u8,
34+
(v0 >> 40) as u8,
35+
(v0 >> 48) as u8,
36+
(v0 >> 56) as u8,
37+
(v1 >> 0) as u8,
38+
(v1 >> 8) as u8,
39+
(v1 >> 16) as u8,
40+
(v1 >> 24) as u8,
41+
(v1 >> 32) as u8,
42+
(v1 >> 40) as u8,
43+
(v1 >> 48) as u8,
44+
(v1 >> 56) as u8,
45+
])
1846
}
1947

2048
#[inline]
21-
pub fn to_smaller_hash(&self) -> u64 {
22-
self.0
49+
pub fn as_value(&self) -> (u64, u64) {
50+
(
51+
((self.0[0] as u64) << 0)
52+
| ((self.0[1] as u64) << 8)
53+
| ((self.0[2] as u64) << 16)
54+
| ((self.0[3] as u64) << 24)
55+
| ((self.0[4] as u64) << 32)
56+
| ((self.0[5] as u64) << 40)
57+
| ((self.0[6] as u64) << 48)
58+
| ((self.0[7] as u64) << 56),
59+
((self.0[8] as u64) << 0)
60+
| ((self.0[9] as u64) << 8)
61+
| ((self.0[10] as u64) << 16)
62+
| ((self.0[11] as u64) << 24)
63+
| ((self.0[12] as u64) << 32)
64+
| ((self.0[13] as u64) << 40)
65+
| ((self.0[14] as u64) << 48)
66+
| ((self.0[15] as u64) << 56),
67+
)
2368
}
2469

2570
#[inline]
26-
pub fn as_value(&self) -> (u64, u64) {
27-
(self.0, self.1)
71+
fn from_value_u128(v: u128) -> Fingerprint {
72+
Fingerprint::from_value(v as u64, (v >> 64) as u64)
73+
}
74+
75+
#[inline]
76+
pub fn as_value_u128(&self) -> u128 {
77+
let (v0, v1) = self.as_value();
78+
v0 as u128 | ((v1 as u128) << 64)
79+
}
80+
81+
#[inline]
82+
pub fn from_smaller_hash(hash: u64) -> Fingerprint {
83+
Fingerprint::from_value(hash, hash)
84+
}
85+
86+
#[inline]
87+
pub fn to_smaller_hash(&self) -> u64 {
88+
self.as_value().0
2889
}
2990

3091
#[inline]
3192
pub fn combine(self, other: Fingerprint) -> Fingerprint {
3293
// See https://stackoverflow.com/a/27952689 on why this function is
3394
// implemented this way.
34-
Fingerprint(
35-
self.0.wrapping_mul(3).wrapping_add(other.0),
36-
self.1.wrapping_mul(3).wrapping_add(other.1),
37-
)
95+
let v = self.as_value_u128().wrapping_mul(3).wrapping_add(other.as_value_u128());
96+
Fingerprint::from_value_u128(v)
3897
}
3998

4099
// Combines two hashes in an order independent way. Make sure this is what
41100
// you want.
42101
#[inline]
43102
pub fn combine_commutative(self, other: Fingerprint) -> Fingerprint {
44-
let a = u128::from(self.1) << 64 | u128::from(self.0);
45-
let b = u128::from(other.1) << 64 | u128::from(other.0);
46-
47-
let c = a.wrapping_add(b);
48-
49-
Fingerprint((c >> 64) as u64, c as u64)
103+
let v = self.as_value_u128().wrapping_add(other.as_value_u128());
104+
Fingerprint::from_value_u128(v)
50105
}
51106

52107
pub fn to_hex(&self) -> String {
53-
format!("{:x}{:x}", self.0, self.1)
108+
let (self0, self1) = self.as_value();
109+
format!("{:x}{:x}", self0, self1)
54110
}
55111

56112
pub fn encode_opaque(&self, encoder: &mut opaque::Encoder) -> EncodeResult {
57-
let bytes: [u8; 16] = unsafe { mem::transmute([self.0.to_le(), self.1.to_le()]) };
58-
59-
encoder.emit_raw_bytes(&bytes);
113+
encoder.emit_raw_bytes(&self.0);
60114
Ok(())
61115
}
62116

63117
pub fn decode_opaque(decoder: &mut opaque::Decoder<'_>) -> Result<Fingerprint, String> {
64-
let mut bytes = [0; 16];
118+
let mut fingerprint = Fingerprint::ZERO;
119+
decoder.read_raw_bytes(&mut fingerprint.0)?;
120+
Ok(fingerprint)
121+
}
122+
}
65123

66-
decoder.read_raw_bytes(&mut bytes)?;
124+
impl std::fmt::Display for Fingerprint {
125+
fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
126+
let (self0, self1) = self.as_value();
127+
write!(formatter, "{:x}-{:x}", self0, self1)
128+
}
129+
}
67130

68-
let [l, r]: [u64; 2] = unsafe { mem::transmute(bytes) };
131+
impl Ord for Fingerprint {
132+
#[inline]
133+
fn cmp(&self, other: &Fingerprint) -> Ordering {
134+
// This implementation is faster than the one generated by the `derive` attribute.
135+
self.as_value_u128().cmp(&other.as_value_u128())
136+
}
137+
}
69138

70-
Ok(Fingerprint(u64::from_le(l), u64::from_le(r)))
139+
impl PartialOrd for Fingerprint {
140+
#[inline]
141+
fn partial_cmp(&self, other: &Fingerprint) -> Option<Ordering> {
142+
// This implementation is faster than the one generated by the `derive` attribute.
143+
Some(self.cmp(other))
71144
}
72145
}
73146

74-
impl std::fmt::Display for Fingerprint {
75-
fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
76-
write!(formatter, "{:x}-{:x}", self.0, self.1)
147+
impl PartialEq for Fingerprint {
148+
#[inline]
149+
fn eq(&self, other: &Fingerprint) -> bool {
150+
// This implementation is faster than the one generated by the `derive` attribute.
151+
self.as_value_u128() == other.as_value_u128()
77152
}
78153
}
79154

@@ -91,24 +166,27 @@ trait FingerprintHasher {
91166
impl<H: Hasher> FingerprintHasher for H {
92167
#[inline]
93168
default fn write_fingerprint(&mut self, fingerprint: &Fingerprint) {
94-
self.write_u64(fingerprint.0);
95-
self.write_u64(fingerprint.1);
169+
// It's faster to hash this as two `u64`s than as a `u128` or slice.
170+
let (fingerprint0, fingerprint1) = fingerprint.as_value();
171+
self.write_u64(fingerprint0);
172+
self.write_u64(fingerprint1);
96173
}
97174
}
98175

99176
impl FingerprintHasher for crate::unhash::Unhasher {
100177
#[inline]
101178
fn write_fingerprint(&mut self, fingerprint: &Fingerprint) {
102179
// `Unhasher` only wants a single `u64`
103-
self.write_u64(fingerprint.0);
180+
let (fingerprint0, _) = fingerprint.as_value();
181+
self.write_u64(fingerprint0);
104182
}
105183
}
106184

107185
impl stable_hasher::StableHasherResult for Fingerprint {
108186
#[inline]
109187
fn finish(hasher: stable_hasher::StableHasher) -> Self {
110188
let (_0, _1) = hasher.finalize();
111-
Fingerprint(_0, _1)
189+
Fingerprint::from_value(_0, _1)
112190
}
113191
}
114192

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
use super::*;
2+
3+
#[test]
4+
fn test_value_roundtrip() {
5+
let f0 = Fingerprint::from_value(0x00221133_44665577, 0x88AA99BB_CCEEDDFF);
6+
let v = f0.as_value();
7+
let f1 = Fingerprint::from_value(v.0, v.1);
8+
assert_eq!(f0, f1);
9+
}
10+
11+
#[test]
12+
fn test_value_u128_roundtrip() {
13+
let f0 = Fingerprint::from_value_u128(0x00221133_44665577_88AA99BB_CCEEDDFF);
14+
let v = f0.as_value_u128();
15+
let f1 = Fingerprint::from_value_u128(v);
16+
assert_eq!(f0, f1);
17+
}
18+
19+
#[test]
20+
fn test_combine_commutative_is_commutative() {
21+
let f0 = Fingerprint::from_value_u128(0x00221133_44665577_88AA99BB_CCEEDDFF);
22+
let f1 = Fingerprint::from_value_u128(0x00112233_44556677_8899AABB_CCDDEEFF);
23+
assert_eq!(f0.combine_commutative(f1), f1.combine_commutative(f0));
24+
}

src/test/ui/async-await/unused-lifetime.stderr

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,18 +10,18 @@ note: the lint level is defined here
1010
LL | #![deny(unused_lifetimes)]
1111
| ^^^^^^^^^^^^^^^^
1212

13-
error: lifetime parameter `'a` never used
14-
--> $DIR/unused-lifetime.rs:31:44
15-
|
16-
LL | pub async fn func_with_two_unused_lifetime<'a, 'b>(s: &'a str, t: &'b str) {
17-
| ^^
18-
1913
error: lifetime parameter `'b` never used
2014
--> $DIR/unused-lifetime.rs:31:48
2115
|
2216
LL | pub async fn func_with_two_unused_lifetime<'a, 'b>(s: &'a str, t: &'b str) {
2317
| ^^
2418

19+
error: lifetime parameter `'a` never used
20+
--> $DIR/unused-lifetime.rs:31:44
21+
|
22+
LL | pub async fn func_with_two_unused_lifetime<'a, 'b>(s: &'a str, t: &'b str) {
23+
| ^^
24+
2525
error: lifetime parameter `'c` never used
2626
--> $DIR/unused-lifetime.rs:37:54
2727
|
Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,26 @@
1-
error: lifetime parameter `'a` only used once
2-
--> $DIR/one-use-in-fn-argument-in-band.rs:11:10
1+
error: lifetime parameter `'b` only used once
2+
--> $DIR/one-use-in-fn-argument-in-band.rs:11:22
33
|
44
LL | fn a(x: &'a u32, y: &'b u32) {
5-
| ^^-
6-
| |
7-
| this lifetime is only used here
8-
| help: elide the single-use lifetime
5+
| ^^-
6+
| |
7+
| this lifetime is only used here
8+
| help: elide the single-use lifetime
99
|
1010
note: the lint level is defined here
1111
--> $DIR/one-use-in-fn-argument-in-band.rs:4:9
1212
|
1313
LL | #![deny(single_use_lifetimes)]
1414
| ^^^^^^^^^^^^^^^^^^^^
1515

16-
error: lifetime parameter `'b` only used once
17-
--> $DIR/one-use-in-fn-argument-in-band.rs:11:22
16+
error: lifetime parameter `'a` only used once
17+
--> $DIR/one-use-in-fn-argument-in-band.rs:11:10
1818
|
1919
LL | fn a(x: &'a u32, y: &'b u32) {
20-
| ^^-
21-
| |
22-
| this lifetime is only used here
23-
| help: elide the single-use lifetime
20+
| ^^-
21+
| |
22+
| this lifetime is only used here
23+
| help: elide the single-use lifetime
2424

2525
error: aborting due to 2 previous errors
2626

0 commit comments

Comments
 (0)