From 2a440328ea19e9646a93f847dd9eff21e03ad16d Mon Sep 17 00:00:00 2001 From: Julian Andres Klode Date: Wed, 15 Jun 2016 23:13:43 +0200 Subject: acquire: Use priority queues and a 3 stage pipeline design Employ a priority queue instead of a normal queue to hold the items; and only add items to the running pipeline if their priority is the same or higher than the priority of items in the queue. The priorities are designed for a 3 stage pipeline system: In stage 1, all Release files and .diff/Index files are fetched. This allows us to determine what files remain to be fetched, and thus ensures a usable progress reporting. In stage 2, all Pdiff patches are fetched, so we can apply them in parallel with fetching other files in stage 3. In stage 3, all other files are fetched (complete index files such as Contents, Packages). Performance improvements, mainly from fetching the pdiff patches before complete files, so they can be applied in parallel: For the 01 Sep 2016 03:35:23 UTC -> 02 Sep 2016 09:25:37 update of Debian unstable and testing with Contents and appstream for amd64 and i386, update time reduced from 37 seconds to 24-28 seconds. Previously, apt would first download new DEP11 icon tarballs and metadata files, causing the CPU to be idle. By fetching the diffs in stage 2, we can now patch our contents and Packages files while we are downloading the DEP11 stuff. --- apt-pkg/acquire.cc | 36 ++++++++++++++++++++++++++++++++---- 1 file changed, 32 insertions(+), 4 deletions(-) (limited to 'apt-pkg/acquire.cc') diff --git a/apt-pkg/acquire.cc b/apt-pkg/acquire.cc index b4d1b5959..2ad6bc47f 100644 --- a/apt-pkg/acquire.cc +++ b/apt-pkg/acquire.cc @@ -894,9 +894,10 @@ pkgAcquire::Queue::~Queue() /* */ bool pkgAcquire::Queue::Enqueue(ItemDesc &Item) { + QItem **OptimalI = &Items; QItem **I = &Items; // move to the end of the queue and check for duplicates here - for (; *I != 0; I = &(*I)->Next) + for (; *I != 0; ) { if (Item.URI == (*I)->URI) { if (_config->FindB("Debug::pkgAcquire::Worker",false) == true) @@ -905,12 +906,22 @@ bool pkgAcquire::Queue::Enqueue(ItemDesc &Item) Item.Owner->Status = (*I)->Owner->Status; return false; } + // Determine the optimal position to insert: before anything with a + // higher priority. + int priority = (*I)->GetPriority(); + + I = &(*I)->Next; + if (priority >= Item.Owner->Priority()) { + OptimalI = I; + } + } + // Create a new item QItem *Itm = new QItem; *Itm = Item; - Itm->Next = 0; - *I = Itm; + Itm->Next = *OptimalI; + *OptimalI = Itm; Item.Owner->QueueCounter++; if (Items->Next == 0) @@ -1060,16 +1071,24 @@ bool pkgAcquire::Queue::Cycle() // Look for a queable item QItem *I = Items; + int ActivePriority = 0; while (PipeDepth < (signed)MaxPipeDepth) { - for (; I != 0; I = I->Next) + for (; I != 0; I = I->Next) { + if (I->Owner->Status == pkgAcquire::Item::StatFetching) + ActivePriority = std::max(ActivePriority, I->GetPriority()); if (I->Owner->Status == pkgAcquire::Item::StatIdle) break; + } // Nothing to do, queue is idle. if (I == 0) return true; + // This item has a lower priority than stuff in the pipeline, pretend + // the queue is idle + if (I->GetPriority() < ActivePriority) + return true; I->Worker = Workers; for (auto const &O: I->Owners) O->Status = pkgAcquire::Item::StatFetching; @@ -1135,6 +1154,15 @@ APT_PURE unsigned long long pkgAcquire::Queue::QItem::GetMaximumSize() const /*{ return Maximum; } /*}}}*/ +APT_PURE int pkgAcquire::Queue::QItem::GetPriority() const /*{{{*/ +{ + int Priority = 0; + for (auto const &O: Owners) + Priority = std::max(Priority, O->Priority()); + + return Priority; +} + /*}}}*/ void pkgAcquire::Queue::QItem::SyncDestinationFiles() const /*{{{*/ { /* ensure that the first owner has the best partial file of all and -- cgit v1.2.3