Skip to content

Commit

Permalink
Change the complexity scaling of reschedule recovered search
Browse files Browse the repository at this point in the history
The reschedule recovered wile searching, for each expiration set, was
performing a loop Recovering number of times to look up in a set of
Expiring sectors. This leads to scaling `O(recovering*log(expiring)).

Following PR changes it such that the work performed is
`O(expiring*log(recovering))`. This should improve the gas usage as on
expectation there is 3-5 sectors expiring in any given queue.

Signed-off-by: Jakub Sztandera <[email protected]>
  • Loading branch information
Jakub Sztandera committed Sep 19, 2022
1 parent 18c2291 commit bd9184e
Showing 1 changed file with 40 additions and 39 deletions.
79 changes: 40 additions & 39 deletions actors/miner/src/expiration_queue.rs
Original file line number Diff line number Diff line change
Expand Up @@ -361,8 +361,8 @@ impl<'db, BS: Blockstore> ExpirationQueue<'db, BS> {
sectors: Vec<SectorOnChainInfo>,
sector_size: SectorSize,
) -> anyhow::Result<PowerPair> {
let mut remaining: BTreeSet<SectorNumber> =
sectors.iter().map(|sector| sector.sector_number).collect();
let mut remaining: BTreeMap<SectorNumber, &SectorOnChainInfo> =
sectors.iter().map(|sector| (sector.sector_number, sector)).collect();

// Traverse the expiration queue once to find each recovering sector and remove it from early/faulty there.
// We expect this to find all recovering sectors within the first FaultMaxAge/WPoStProvingPeriod entries
Expand All @@ -371,48 +371,49 @@ impl<'db, BS: Blockstore> ExpirationQueue<'db, BS> {
let mut recovered_power = PowerPair::zero();

self.iter_while_mut(|_epoch, expiration_set| {
let on_time_sectors: BTreeSet<SectorNumber> = expiration_set
.on_time_sectors
.bounded_iter(ENTRY_SECTORS_MAX)
.context("too many sectors to reschedule")?
.map(|i| i as SectorNumber)
.collect();
let mut faulty_power_delta = PowerPair::zero();
let mut active_power_delta = PowerPair::zero();

let early_sectors: BTreeSet<SectorNumber> = expiration_set
.early_sectors
.bounded_iter(ENTRY_SECTORS_MAX)
.context("too many sectors to reschedule")?
.map(|i| i as SectorNumber)
.collect();
for sector_number in expiration_set.on_time_sectors.iter() {
let sector = match remaining.remove(&sector_number) {
Some(s) => s,
None => continue,
};

// This loop could alternatively be done by constructing bitfields and intersecting them, but it's not
// clear that would be much faster (O(max(N, M)) vs O(N+M)).
// If faults are correlated, the first queue entry likely has them all anyway.
// The length of sectors has a maximum of one partition size.
for sector in sectors.iter() {
let sector_number = sector.sector_number;
// If the sector expires on-time at this epoch, leave it here but change faulty power to active.
// The pledge is already part of the on-time pledge at this entry.
let power = power_for_sector(sector_size, sector);
let mut found = false;
faulty_power_delta -= &power;
active_power_delta += &power;

if on_time_sectors.contains(&sector_number) {
found = true;
// If the sector expires on-time at this epoch, leave it here but change faulty power to active.
// The pledge is already part of the on-time pledge at this entry.
expiration_set.faulty_power -= &power;
expiration_set.active_power += &power;
} else if early_sectors.contains(&sector_number) {
found = true;
// If the sector expires early at this epoch, remove it for re-scheduling.
// It's not part of the on-time pledge number here.
expiration_set.early_sectors.unset(sector_number);
expiration_set.faulty_power -= &power;
sectors_rescheduled.push(sector);
}
recovered_power += &power;
}
let mut early_unset = Vec::new();
for sector_number in expiration_set.early_sectors.iter() {
let sector = match remaining.remove(&sector_number) {
Some(s) => s,
None => continue,
};
// If the sector expires early at this epoch, remove it for re-scheduling.
// It's not part of the on-time pledge number here.
early_unset.push(sector_number);
let power = power_for_sector(sector_size, sector);
faulty_power_delta -= &power;
sectors_rescheduled.push(sector);

if found {
recovered_power += &power;
remaining.remove(&sector.sector_number);
}
recovered_power += &power;
}

// we need to defer the changes as we cannot borrow immutably for iteration
// and mutably for changes at the same time
if !early_unset.is_empty()
|| !faulty_power_delta.is_zero()
|| !active_power_delta.is_zero()
{
expiration_set.active_power += &active_power_delta;
expiration_set.faulty_power += &faulty_power_delta;

expiration_set.early_sectors -= BitField::try_from_bits(early_unset)?;
}

expiration_set.validate_state()?;
Expand Down

0 comments on commit bd9184e

Please sign in to comment.