[gnome-music/wip/mschraal/file-exists-async: 1/3] asyncqueue: Add adaptive queueing




commit 31b1207c2198a468656a645557502eadc6f21c34
Author: Marinus Schraal <mschraal gnome org>
Date:   Tue Aug 17 16:25:00 2021 +0200

    asyncqueue: Add adaptive queueing
    
    Provide adaptive queueing, which throttles the amount of concurrent
    async tasks based on earlier timings.
    
    This is mainly meant for local I/O based tasks as the timings may
    indicate bottlenecks.

 gnomemusic/albumart.py         |  2 +-
 gnomemusic/artistart.py        |  2 +-
 gnomemusic/asyncqueue.py       | 23 ++++++++++++++++++++++-
 gnomemusic/songart.py          |  2 +-
 gnomemusic/widgets/artstack.py |  2 +-
 5 files changed, 26 insertions(+), 5 deletions(-)
---
diff --git a/gnomemusic/albumart.py b/gnomemusic/albumart.py
index 7a8f6a12a..fc5d4ce39 100644
--- a/gnomemusic/albumart.py
+++ b/gnomemusic/albumart.py
@@ -35,7 +35,7 @@ class AlbumArt(GObject.GObject):
     """AlbumArt retrieval object
     """
 
-    _async_queue = AsyncQueue("AlbumArt")
+    _async_queue = AsyncQueue("AlbumArt", True)
 
     def __init__(self, application, corealbum):
         """Initialize AlbumArt
diff --git a/gnomemusic/artistart.py b/gnomemusic/artistart.py
index 04d913c76..ec95fd8b0 100644
--- a/gnomemusic/artistart.py
+++ b/gnomemusic/artistart.py
@@ -34,7 +34,7 @@ class ArtistArt(GObject.GObject):
     """Artist art retrieval object
     """
 
-    _async_queue = AsyncQueue("ArtistArt")
+    _async_queue = AsyncQueue("ArtistArt", True)
 
     def __init__(self, application, coreartist):
         """Initialize.
diff --git a/gnomemusic/asyncqueue.py b/gnomemusic/asyncqueue.py
index 3c7dd6d8a..2709f9e6a 100644
--- a/gnomemusic/asyncqueue.py
+++ b/gnomemusic/asyncqueue.py
@@ -22,6 +22,7 @@
 # code, but you are not obligated to do so.  If you do not wish to do so,
 # delete this exception statement from your version.
 
+from collections import deque
 from typing import Any, Dict, Optional, Tuple
 import time
 
@@ -46,19 +47,25 @@ class AsyncQueue(GObject.GObject):
     may have an arbitrary number of arguments following.
     """
 
-    def __init__(self, queue_name: Optional[str] = None) -> None:
+    def __init__(
+            self, queue_name: Optional[str] = None,
+            adaptive: bool = False) -> None:
         """Initialize AsyncQueue
 
         :param str queue_name: The user facing name of this queue or
             None for the generic class identifier
+        :param bool adaptive: Allow adapting the queue size based on
+            timing information
         """
         super().__init__()
 
+        self._adaptive = adaptive
         self._async_pool: Dict[int, Tuple] = {}
         self._async_active_pool: Dict[int, Tuple] = {}
         self._log = MusicLogger()
         self._max_async = 4
         self._queue_name = queue_name if queue_name else self
+        self._timings = deque([0], 10)
 
     def queue(self, *args: Any) -> None:
         """Queue an async call
@@ -89,6 +96,9 @@ class AsyncQueue(GObject.GObject):
                 f"{self._queue_name}: "
                 f"{a} active task(s) of {len(self._async_pool) + a}")
 
+            if self._adaptive:
+                self._adapt_queue_size(t)
+
             async_obj = args[0]
             async_obj.disconnect(result_id)
             self._async_active_pool.pop(id(async_obj))
@@ -105,3 +115,14 @@ class AsyncQueue(GObject.GObject):
 
             result_id = async_obj.connect("finished", on_async_finished)
             async_obj.start(*args[1:])
+
+    def _adapt_queue_size(self, timing):
+        avg_time = sum(self._timings) / len(self._timings)
+        if avg_time > timing * 1.25:
+            self._max_async -= 1
+            self._max_async = max(1, self._max_async)
+        elif avg_time < timing * 0.75:
+            self._max_async += 1
+            self._max_async = min(10, self._max_async)
+
+        self._timings.append(timing)
diff --git a/gnomemusic/songart.py b/gnomemusic/songart.py
index 94bf46c90..b18a9d10e 100644
--- a/gnomemusic/songart.py
+++ b/gnomemusic/songart.py
@@ -35,7 +35,7 @@ class SongArt(GObject.GObject):
     """SongArt retrieval object
     """
 
-    _async_queue = AsyncQueue("SongArt")
+    _async_queue = AsyncQueue("SongArt", True)
 
     def __init__(self, application, coresong):
         """Initialize SongArt
diff --git a/gnomemusic/widgets/artstack.py b/gnomemusic/widgets/artstack.py
index 0c0b9d379..7439bfb2c 100644
--- a/gnomemusic/widgets/artstack.py
+++ b/gnomemusic/widgets/artstack.py
@@ -40,7 +40,7 @@ class ArtStack(Gtk.Stack):
 
     __gtype_name__ = "ArtStack"
 
-    _async_queue = AsyncQueue("ArtStack")
+    _async_queue = AsyncQueue("ArtStack", True)
 
     def __init__(self, size: ArtSize = ArtSize.MEDIUM) -> None:
         """Initialize the ArtStack


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]