libcamera: V4L2BufferCache: Improve cache eviction strategy

The strategy used to find a free cache entry in the first implementation
was not the smartest, it picked the first free entry. This lead to
unwanted performance issues as the cache was not used as good as it
could for imported buffers.

Improve this by adding a last usage sequence number to the cache entries
and change the eviction strategy to use the oldest free entry instead of
the first one it finds.

Signed-off-by: Niklas Söderlund <niklas.soderlund@ragnatech.se>
Reviewed-by: Naushir Patuck <naush@raspberrypi.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
This commit is contained in:
Niklas Söderlund 2020-02-16 16:51:53 +01:00
parent 69d1e24ac7
commit 4e0d1eca10
2 changed files with 20 additions and 10 deletions

View file

@ -7,11 +7,12 @@
#ifndef __LIBCAMERA_V4L2_VIDEODEVICE_H__
#define __LIBCAMERA_V4L2_VIDEODEVICE_H__
#include <atomic>
#include <memory>
#include <string>
#include <vector>
#include <linux/videodev2.h>
#include <memory>
#include <libcamera/buffer.h>
#include <libcamera/geometry.h>
@ -120,11 +121,12 @@ private:
{
public:
Entry();
Entry(bool free, const FrameBuffer &buffer);
Entry(bool free, uint64_t lastUsed, const FrameBuffer &buffer);
bool operator==(const FrameBuffer &buffer) const;
bool free;
uint64_t lastUsed;
private:
struct Plane {
@ -140,6 +142,7 @@ private:
std::vector<Plane> planes_;
};
std::atomic_uint64_t lastUsedCounter_;
std::vector<Entry> cache_;
/* \todo Expose the miss counter through an instrumentation API. */
unsigned int missCounter_;

View file

@ -162,7 +162,7 @@ LOG_DECLARE_CATEGORY(V4L2)
* buffer import, with buffers added to the cache as they are queued.
*/
V4L2BufferCache::V4L2BufferCache(unsigned int numEntries)
: missCounter_(0)
: lastUsedCounter_(1), missCounter_(0)
{
cache_.resize(numEntries);
}
@ -176,10 +176,12 @@ V4L2BufferCache::V4L2BufferCache(unsigned int numEntries)
* allocated.
*/
V4L2BufferCache::V4L2BufferCache(const std::vector<std::unique_ptr<FrameBuffer>> &buffers)
: missCounter_(0)
: lastUsedCounter_(1), missCounter_(0)
{
for (const std::unique_ptr<FrameBuffer> &buffer : buffers)
cache_.emplace_back(true, buffer->planes());
cache_.emplace_back(true,
lastUsedCounter_.fetch_add(1, std::memory_order_acq_rel),
buffer->planes());
}
V4L2BufferCache::~V4L2BufferCache()
@ -205,6 +207,7 @@ int V4L2BufferCache::get(const FrameBuffer &buffer)
{
bool hit = false;
int use = -1;
uint64_t oldest = UINT64_MAX;
for (unsigned int index = 0; index < cache_.size(); index++) {
const Entry &entry = cache_[index];
@ -219,8 +222,10 @@ int V4L2BufferCache::get(const FrameBuffer &buffer)
break;
}
if (use < 0)
if (entry.lastUsed < oldest) {
use = index;
oldest = entry.lastUsed;
}
}
if (!hit)
@ -229,7 +234,9 @@ int V4L2BufferCache::get(const FrameBuffer &buffer)
if (use < 0)
return -ENOENT;
cache_[use] = Entry(false, buffer);
cache_[use] = Entry(false,
lastUsedCounter_.fetch_add(1, std::memory_order_acq_rel),
buffer);
return use;
}
@ -245,12 +252,12 @@ void V4L2BufferCache::put(unsigned int index)
}
V4L2BufferCache::Entry::Entry()
: free(true)
: free(true), lastUsed(0)
{
}
V4L2BufferCache::Entry::Entry(bool free, const FrameBuffer &buffer)
: free(free)
V4L2BufferCache::Entry::Entry(bool free, uint64_t lastUsed, const FrameBuffer &buffer)
: free(free), lastUsed(lastUsed)
{
for (const FrameBuffer::Plane &plane : buffer.planes())
planes_.emplace_back(plane);