Show More
@@ -1044,7 +1044,7 b' impl Index {' | |||||
1044 | /// greatest common ancestor. In revset notation, this is the set |
|
1044 | /// greatest common ancestor. In revset notation, this is the set | |
1045 | /// `heads(::a and ::b and ...)` |
|
1045 | /// `heads(::a and ::b and ...)` | |
1046 | #[allow(dead_code)] |
|
1046 | #[allow(dead_code)] | |
1047 | fn find_gca_candidates( |
|
1047 | fn find_gca_candidates<BS: PoisonableBitSet + Clone>( | |
1048 | &self, |
|
1048 | &self, | |
1049 | revs: &[Revision], |
|
1049 | revs: &[Revision], | |
1050 | ) -> Result<Vec<Revision>, GraphError> { |
|
1050 | ) -> Result<Vec<Revision>, GraphError> { | |
@@ -1053,13 +1053,12 b' impl Index {' | |||||
1053 | } |
|
1053 | } | |
1054 | let revcount = revs.len(); |
|
1054 | let revcount = revs.len(); | |
1055 | let mut candidates = vec![]; |
|
1055 | let mut candidates = vec![]; | |
1056 | let all_seen = 1u64 << (revcount - 1); |
|
|||
1057 | let poison = 1u64 << revs.len(); |
|
|||
1058 | let max_rev = revs.iter().max().unwrap(); |
|
1056 | let max_rev = revs.iter().max().unwrap(); | |
1059 | let mut seen = vec![0u64; (max_rev.0 + 1) as usize]; |
|
1057 | ||
|
1058 | let mut seen = BS::vec_of_empty(revs.len(), (max_rev.0 + 1) as usize); | |||
1060 |
|
1059 | |||
1061 | for (idx, rev) in revs.iter().enumerate() { |
|
1060 | for (idx, rev) in revs.iter().enumerate() { | |
1062 |
seen[rev.0 as usize] |
|
1061 | seen[rev.0 as usize].add(idx); | |
1063 | } |
|
1062 | } | |
1064 | let mut current_rev = *max_rev; |
|
1063 | let mut current_rev = *max_rev; | |
1065 | // Number of revisions whose inspection in the main loop |
|
1064 | // Number of revisions whose inspection in the main loop | |
@@ -1089,17 +1088,18 b' impl Index {' | |||||
1089 | // - Early return in case it is detected that one of the incoming revs |
|
1088 | // - Early return in case it is detected that one of the incoming revs | |
1090 | // is a common ancestor of all of them. |
|
1089 | // is a common ancestor of all of them. | |
1091 | while current_rev.0 >= 0 && interesting > 0 { |
|
1090 | while current_rev.0 >= 0 && interesting > 0 { | |
1092 | let mut current_seen = seen[current_rev.0 as usize]; |
|
1091 | let mut current_seen = seen[current_rev.0 as usize].clone(); | |
1093 |
|
1092 | |||
1094 |
if current_seen |
|
1093 | if current_seen.is_empty() { | |
1095 | current_rev = Revision(current_rev.0 - 1); |
|
1094 | current_rev = Revision(current_rev.0 - 1); | |
1096 | continue; |
|
1095 | continue; | |
1097 | } |
|
1096 | } | |
1098 |
if current_seen |
|
1097 | if !current_seen.is_poisoned() { | |
1099 | interesting -= 1; |
|
1098 | interesting -= 1; | |
1100 |
if current_seen |
|
1099 | if current_seen.is_full_range(revcount) { | |
1101 | candidates.push(current_rev); |
|
1100 | candidates.push(current_rev); | |
1102 |
current_ |
|
1101 | seen[current_rev.0 as usize].poison(); | |
|
1102 | current_seen.poison(); // avoid recloning | |||
1103 |
|
1103 | |||
1104 | // Being a common ancestor, if `current_rev` is among |
|
1104 | // Being a common ancestor, if `current_rev` is among | |
1105 | // the input revisions, it is *the* answer. |
|
1105 | // the input revisions, it is *the* answer. | |
@@ -1114,26 +1114,28 b' impl Index {' | |||||
1114 | if parent == NULL_REVISION { |
|
1114 | if parent == NULL_REVISION { | |
1115 | continue; |
|
1115 | continue; | |
1116 | } |
|
1116 | } | |
1117 | let parent_seen = seen[parent.0 as usize]; |
|
1117 | let parent_seen = &seen[parent.0 as usize]; | |
1118 |
if current_seen |
|
1118 | if !current_seen.is_poisoned() { | |
1119 | // Without the `interesting` accounting, this block would |
|
1119 | // Without the `interesting` accounting, this block would | |
1120 | // be logically equivalent to: parent_seen |= current_seen |
|
1120 | // be logically equivalent to: parent_seen |= current_seen | |
1121 | // The parent counts as interesting if it was not already |
|
1121 | // The parent counts as interesting if it was not already | |
1122 | // known to be an ancestor (would already have counted) |
|
1122 | // known to be an ancestor (would already have counted) | |
1123 |
if parent_seen |
|
1123 | if parent_seen.is_empty() { | |
1124 | seen[parent.0 as usize] = current_seen; |
|
1124 | seen[parent.0 as usize] = current_seen.clone(); | |
1125 | interesting += 1; |
|
1125 | interesting += 1; | |
1126 | } else if parent_seen != current_seen { |
|
1126 | } else if *parent_seen != current_seen { | |
1127 |
seen[parent.0 as usize] |
|
1127 | seen[parent.0 as usize].union(¤t_seen); | |
1128 | } |
|
1128 | } | |
1129 | } else { |
|
1129 | } else { | |
1130 | // this block is logically equivalent to poisoning parent |
|
1130 | // this block is logically equivalent to poisoning parent | |
1131 | // and counting it as non interesting if it |
|
1131 | // and counting it as non interesting if it | |
1132 | // has been seen before (hence counted then as interesting) |
|
1132 | // has been seen before (hence counted then as interesting) | |
1133 |
if parent_seen |
|
1133 | if !parent_seen.is_empty() && !parent_seen.is_poisoned() { | |
1134 | interesting -= 1; |
|
1134 | interesting -= 1; | |
1135 | } |
|
1135 | } | |
1136 | seen[parent.0 as usize] = current_seen; |
|
1136 | // equivalent to poisoning parent, which we should do to | |
|
1137 | // avoid the cloning. | |||
|
1138 | seen[parent.0 as usize] = current_seen.clone(); | |||
1137 | } |
|
1139 | } | |
1138 | } |
|
1140 | } | |
1139 |
|
1141 | |||
@@ -1242,6 +1244,189 b' impl Index {' | |||||
1242 | } |
|
1244 | } | |
1243 | } |
|
1245 | } | |
1244 |
|
1246 | |||
|
1247 | /// The kind of functionality needed by find_gca_candidates | |||
|
1248 | /// | |||
|
1249 | /// This is a bit mask which can be declared to be "poisoned", which callers | |||
|
1250 | /// interpret to break out of some loops. | |||
|
1251 | /// | |||
|
1252 | /// The maximum capacity of the bit mask is up to the actual implementation | |||
|
1253 | trait PoisonableBitSet: Sized + PartialEq { | |||
|
1254 | /// Return a vector of exactly n elements, initialized to be empty. | |||
|
1255 | /// | |||
|
1256 | /// Optimization can vastly depend on implementation. Those being `Copy` | |||
|
1257 | /// and having constant capacity typically can have a very simple | |||
|
1258 | /// implementation. | |||
|
1259 | fn vec_of_empty(sets_size: usize, vec_len: usize) -> Vec<Self>; | |||
|
1260 | ||||
|
1261 | /// The size of the bit mask in memory | |||
|
1262 | fn size(&self) -> usize; | |||
|
1263 | ||||
|
1264 | /// The number of elements that can be represented in the set. | |||
|
1265 | /// | |||
|
1266 | /// Another way to put it is that it is the highest integer `C` such that | |||
|
1267 | /// the set is guaranteed to always be a subset of the integer range | |||
|
1268 | /// `[0, C)` | |||
|
1269 | fn capacity(&self) -> usize; | |||
|
1270 | ||||
|
1271 | /// Declare `n` to belong to the set | |||
|
1272 | fn add(&mut self, n: usize); | |||
|
1273 | ||||
|
1274 | /// Declare `n` not to belong to the set | |||
|
1275 | fn discard(&mut self, n: usize); | |||
|
1276 | ||||
|
1277 | /// Replace this bit set by its union with other | |||
|
1278 | fn union(&mut self, other: &Self); | |||
|
1279 | ||||
|
1280 | /// Poison the bit set | |||
|
1281 | /// | |||
|
1282 | /// Interpretation up to the caller | |||
|
1283 | fn poison(&mut self); | |||
|
1284 | ||||
|
1285 | /// Is the bit set poisoned? | |||
|
1286 | /// | |||
|
1287 | /// Interpretation is up to the caller | |||
|
1288 | fn is_poisoned(&self) -> bool; | |||
|
1289 | ||||
|
1290 | /// Is the bit set empty? | |||
|
1291 | fn is_empty(&self) -> bool; | |||
|
1292 | ||||
|
1293 | /// return `true` if and only if the bit is the full range `[0, n)` | |||
|
1294 | /// of integers | |||
|
1295 | fn is_full_range(&self, n: usize) -> bool; | |||
|
1296 | } | |||
|
1297 | ||||
|
1298 | const U64_POISON: u64 = 1 << 63; | |||
|
1299 | ||||
|
1300 | impl PoisonableBitSet for u64 { | |||
|
1301 | fn vec_of_empty(_sets_size: usize, vec_len: usize) -> Vec<Self> { | |||
|
1302 | vec![0u64; vec_len] | |||
|
1303 | } | |||
|
1304 | ||||
|
1305 | fn size(&self) -> usize { | |||
|
1306 | 8 | |||
|
1307 | } | |||
|
1308 | ||||
|
1309 | fn capacity(&self) -> usize { | |||
|
1310 | 63 | |||
|
1311 | } | |||
|
1312 | ||||
|
1313 | fn add(&mut self, n: usize) { | |||
|
1314 | (*self) |= 1u64 << n; | |||
|
1315 | } | |||
|
1316 | ||||
|
1317 | fn discard(&mut self, n: usize) { | |||
|
1318 | (*self) &= u64::MAX - (1u64 << n); | |||
|
1319 | } | |||
|
1320 | ||||
|
1321 | fn union(&mut self, other: &Self) { | |||
|
1322 | (*self) |= *other; | |||
|
1323 | } | |||
|
1324 | ||||
|
1325 | fn is_full_range(&self, n: usize) -> bool { | |||
|
1326 | *self + 1 == (1u64 << n) | |||
|
1327 | } | |||
|
1328 | ||||
|
1329 | fn is_empty(&self) -> bool { | |||
|
1330 | *self == 0 | |||
|
1331 | } | |||
|
1332 | ||||
|
1333 | fn poison(&mut self) { | |||
|
1334 | *self = U64_POISON; | |||
|
1335 | } | |||
|
1336 | ||||
|
1337 | fn is_poisoned(&self) -> bool { | |||
|
1338 | // equality comparison would be tempting but would not resist | |||
|
1339 | // operations after poisoning (even if these should be bogus). | |||
|
1340 | *self >= U64_POISON | |||
|
1341 | } | |||
|
1342 | } | |||
|
1343 | ||||
|
1344 | /// A poisonable bit set whose capacity is not known at compile time but | |||
|
1345 | /// is constant after initial construction | |||
|
1346 | /// | |||
|
1347 | /// This can be way further optimized if performance assessments (speed | |||
|
1348 | /// and/or RAM) require it. | |||
|
1349 | /// As far as RAM is concerned, for large vectors of these, the main problem | |||
|
1350 | /// would be the repetition of set_size in each item. We would need a trait | |||
|
1351 | /// to abstract over the idea of a vector of such bit sets to do better. | |||
|
1352 | #[derive(Clone, PartialEq)] | |||
|
1353 | struct NonStaticPoisonableBitSet { | |||
|
1354 | set_size: usize, | |||
|
1355 | bit_set: Vec<u64>, | |||
|
1356 | } | |||
|
1357 | ||||
|
1358 | /// Number of `u64` needed for a [`NonStaticPoisonableBitSet`] of given size | |||
|
1359 | fn non_static_poisonable_inner_len(set_size: usize) -> usize { | |||
|
1360 | 1 + (set_size + 1) / 64 | |||
|
1361 | } | |||
|
1362 | ||||
|
1363 | impl NonStaticPoisonableBitSet { | |||
|
1364 | /// The index of the sub-bit set for the given n, and the index inside | |||
|
1365 | /// the latter | |||
|
1366 | fn index(&self, n: usize) -> (usize, usize) { | |||
|
1367 | (n / 64, n % 64) | |||
|
1368 | } | |||
|
1369 | } | |||
|
1370 | ||||
|
1371 | /// Mock implementation to ensure that the trait makes sense | |||
|
1372 | impl PoisonableBitSet for NonStaticPoisonableBitSet { | |||
|
1373 | fn vec_of_empty(set_size: usize, vec_len: usize) -> Vec<Self> { | |||
|
1374 | let tmpl = Self { | |||
|
1375 | set_size, | |||
|
1376 | bit_set: vec![0u64; non_static_poisonable_inner_len(set_size)], | |||
|
1377 | }; | |||
|
1378 | vec![tmpl; vec_len] | |||
|
1379 | } | |||
|
1380 | ||||
|
1381 | fn size(&self) -> usize { | |||
|
1382 | 8 + self.bit_set.len() * 8 | |||
|
1383 | } | |||
|
1384 | ||||
|
1385 | fn capacity(&self) -> usize { | |||
|
1386 | self.set_size | |||
|
1387 | } | |||
|
1388 | ||||
|
1389 | fn add(&mut self, n: usize) { | |||
|
1390 | let (sub_bs, bit_pos) = self.index(n); | |||
|
1391 | self.bit_set[sub_bs] |= 1 << bit_pos | |||
|
1392 | } | |||
|
1393 | ||||
|
1394 | fn discard(&mut self, n: usize) { | |||
|
1395 | let (sub_bs, bit_pos) = self.index(n); | |||
|
1396 | self.bit_set[sub_bs] |= u64::MAX - (1 << bit_pos) | |||
|
1397 | } | |||
|
1398 | ||||
|
1399 | fn union(&mut self, other: &Self) { | |||
|
1400 | assert!( | |||
|
1401 | self.set_size == other.set_size, | |||
|
1402 | "Binary operations on bit sets can only be done on same size" | |||
|
1403 | ); | |||
|
1404 | for i in 0..self.bit_set.len() - 1 { | |||
|
1405 | self.bit_set[i] |= other.bit_set[i] | |||
|
1406 | } | |||
|
1407 | } | |||
|
1408 | ||||
|
1409 | fn is_full_range(&self, n: usize) -> bool { | |||
|
1410 | let (sub_bs, bit_pos) = self.index(n); | |||
|
1411 | self.bit_set[..sub_bs].iter().all(|bs| *bs == u64::MAX) | |||
|
1412 | && self.bit_set[sub_bs] == (1 << (bit_pos + 1)) - 1 | |||
|
1413 | } | |||
|
1414 | ||||
|
1415 | fn is_empty(&self) -> bool { | |||
|
1416 | self.bit_set.iter().all(|bs| *bs == 0u64) | |||
|
1417 | } | |||
|
1418 | ||||
|
1419 | fn poison(&mut self) { | |||
|
1420 | let (sub_bs, bit_pos) = self.index(self.set_size); | |||
|
1421 | self.bit_set[sub_bs] = 1 << bit_pos; | |||
|
1422 | } | |||
|
1423 | ||||
|
1424 | fn is_poisoned(&self) -> bool { | |||
|
1425 | let (sub_bs, bit_pos) = self.index(self.set_size); | |||
|
1426 | self.bit_set[sub_bs] >= 1 << bit_pos | |||
|
1427 | } | |||
|
1428 | } | |||
|
1429 | ||||
1245 | /// Set of roots of all non-public phases |
|
1430 | /// Set of roots of all non-public phases | |
1246 | pub type RootsPerPhase = [HashSet<Revision>; Phase::non_public_phases().len()]; |
|
1431 | pub type RootsPerPhase = [HashSet<Revision>; Phase::non_public_phases().len()]; | |
1247 |
|
1432 |
General Comments 0
You need to be logged in to leave comments.
Login now