预取项目并立即返回请求的项目

时间:2018-04-02 06:04:03

标签: python caching pyqt5 background-process prefetch

我需要从网络共享中加载大量大型图像数据进行处理(速度不是很快)。图像按照序列命名(例如1.png,2.png,3.png等)。

在大多数情况下,加载将按此顺序进行(在n.png之后加载n + 1.png)。我希望在实际请求之前在内存中有n + 1.png。

我想保留一个缓存(也是如此),这样返回1张图片就不需要磁盘访问了。

我设想这样的事情:

  1. 请求索引为n
  2. 的图片
  3. 检查n.png是否在缓存中,如果图像不在缓存中: 一个。从磁盘加载图像 湾将图像放入缓存
  4. 对索引为n + 1
  5. 的图像执行步骤1和2
  6. 等待第3步完成,但从缓存中获取图像并返回该图像
  7. 很高兴有功能:清理背景中的缓存,使其仅包含最后请求的10个项目,或者删除第一个请求的项目,直到它包含最大值。 10个项目(我可以想象后一个选项更容易实现,同时对我的情况足够好)。

    我使用的是Python 3.5。我正在使用PyQt5,但我更喜欢这个函数不依赖于PyQt5的功能(但是如果这使得实现更加干净/简单/可读,我将使用它。)

2 个答案:

答案 0 :(得分:0)

简单的答案(假设你没有使用协同程序等,你可能没有给出你使用PyQt5)是为了生成一个守护进程后台线程来加载图像n + 1进入缓存。像这样:

def load(self, n):
    with self._cache_lock:
        try:
            return self._cache[n]
        except KeyError:
            self._cache[n] = self._load_image(n)
            return self._cache[n]
def process_image(self, n):
    img = self.load(n)
    threading.Thread(target=partial(self.load, n+1), daemon=True).start()
    self.step1(img)
    self.step2(img)

此设计的问题在于您围绕整个_load操作锁定。如果step1step2显着超过_load_image,则通过允许罕见的重复工作来避免锁定可能更便宜:

def cacheget(self, n):
    with self._cache_lock:
        return self._cache.get(n)
def preload(self, n):
    img = self._load_image(n)
    with self._cache_lock:
        self._cache[n] = img
    return img
def process_image(self, n):
    img = self.cacheget(n)
    if img is None:
        img = self.preload(n)
    threading.Thread(target=partial(self.preload, n+1), daemon=True).start()
    self.step1(img)
    self.step2(img)

如果您希望并行执行大量处理,则可能需要使用ThreadPoolExecutor来排队所有预加载,而不是每个加载的守护程序线程。

如果要清除旧缓存值,请参阅lru_cacheits implementation。有很多调整决策要做(比如:你真的想要后台缓存垃圾收集,或者你可以按照lru_cache的方式添加第10个项目时将最旧的项目推出去吗?),但没有一旦你决定了你想要的东西,这些选项就特别难以构建。

答案 1 :(得分:0)

我开发了library,我相信它确实可以做到:

files = ['1.png', '2.png', '3.png']
images = seqtools.smap(load_img, files)  # (on-demand) image loading
images = seqtools.prefetch(images, max_buffered=10, method='thread')  # prefetching
images = seqtools.add_cache(images, cache_size=10)  # lru cache
image[0]
images[1]

预取的工作方式是使对最后一个请求项之后的项的评估排队。

线程/进程和内存清理是自动处理的。

请注意,上面的代码在内存中有20张图像:10张用于预取缓冲区,而10张用于最近请求的项目。

最后,您可能要根据加载函数的功能在线程后端和多处理后端之间进行切换。