[libcamera-devel,1/2] libcamera: V4L2BufferCache: Improve cache eviction strategy

Message ID 20200216161642.152263-2-niklas.soderlund@ragnatech.se
State Superseded
Headers show
Series
  • libcamera: V4L2BufferCache: Improve cache eviction strategy
Related show

Commit Message

Niklas Söderlund Feb. 16, 2020, 4:16 p.m. UTC
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 they cache was not used as good as it
could for imported buffers.

Improve this by adding a last usage timestamp 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>
---
 src/libcamera/include/v4l2_videodevice.h | 1 +
 src/libcamera/v4l2_videodevice.cpp       | 9 ++++++---
 2 files changed, 7 insertions(+), 3 deletions(-)

Comments

Laurent Pinchart Feb. 16, 2020, 9:43 p.m. UTC | #1
Hi Niklas,

Thank you for the patch.

On Sun, Feb 16, 2020 at 05:16:41PM +0100, Niklas Söderlund wrote:
> 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 they cache was not used as good as it
> could for imported buffers.
> 
> Improve this by adding a last usage timestamp to the cache entries and
> change the eviction strategy to use the oldest free entry instead of the
> first one it finds.

Wouldn't it be simpler to as a LRU mechanism, to avoid searching in the
whole cache every time ?

> Signed-off-by: Niklas Söderlund <niklas.soderlund@ragnatech.se>
> ---
>  src/libcamera/include/v4l2_videodevice.h | 1 +
>  src/libcamera/v4l2_videodevice.cpp       | 9 ++++++---
>  2 files changed, 7 insertions(+), 3 deletions(-)
> 
> diff --git a/src/libcamera/include/v4l2_videodevice.h b/src/libcamera/include/v4l2_videodevice.h
> index fcf072641420dacf..f04feed87b24f28f 100644
> --- a/src/libcamera/include/v4l2_videodevice.h
> +++ b/src/libcamera/include/v4l2_videodevice.h
> @@ -125,6 +125,7 @@ private:
>  		bool operator==(const FrameBuffer &buffer);
>  
>  		bool free;
> +		utils::time_point lastUsed;
>  
>  	private:
>  		struct Plane {
> diff --git a/src/libcamera/v4l2_videodevice.cpp b/src/libcamera/v4l2_videodevice.cpp
> index 99470ce11421c77c..9a4d2479b20f5e27 100644
> --- a/src/libcamera/v4l2_videodevice.cpp
> +++ b/src/libcamera/v4l2_videodevice.cpp
> @@ -205,6 +205,7 @@ int V4L2BufferCache::get(const FrameBuffer &buffer)
>  {
>  	bool hit = false;
>  	int use = -1;
> +	utils::time_point oldest = utils::clock::now();
>  
>  	for (unsigned int index = 0; index < cache_.size(); index++) {
>  		const Entry &entry = cache_[index];
> @@ -212,8 +213,10 @@ int V4L2BufferCache::get(const FrameBuffer &buffer)
>  		if (!entry.free)
>  			continue;
>  
> -		if (use < 0)
> +		if (cache_[index].lastUsed < oldest) {
>  			use = index;
> +			oldest = cache_[index].lastUsed;
> +		}
>  
>  		/* Try to find a cache hit by comparing the planes. */
>  		if (cache_[index] == buffer) {
> @@ -245,12 +248,12 @@ void V4L2BufferCache::put(unsigned int index)
>  }
>  
>  V4L2BufferCache::Entry::Entry()
> -	: free(true)
> +	: free(true), lastUsed(utils::clock::now())
>  {
>  }
>  
>  V4L2BufferCache::Entry::Entry(bool free, const FrameBuffer &buffer)
> -	: free(free)
> +	: free(free), lastUsed(utils::clock::now())
>  {
>  	for (const FrameBuffer::Plane &plane : buffer.planes())
>  		planes_.emplace_back(plane);
Niklas Söderlund Feb. 17, 2020, 12:36 p.m. UTC | #2
Hi Laurent,

Thanks for your feedback.

On 2020-02-16 23:43:11 +0200, Laurent Pinchart wrote:
> Hi Niklas,
> 
> Thank you for the patch.
> 
> On Sun, Feb 16, 2020 at 05:16:41PM +0100, Niklas Söderlund wrote:
> > 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 they cache was not used as good as it
> > could for imported buffers.
> > 
> > Improve this by adding a last usage timestamp to the cache entries and
> > change the eviction strategy to use the oldest free entry instead of the
> > first one it finds.
> 
> Wouldn't it be simpler to as a LRU mechanism, to avoid searching in the
> whole cache every time ?

It's an option, but we need to search the whole cache anyhow to see if 
there is a hot entry in their that should be reused. The change in this 
patch simply piggyback on that walk and provides the best fallback if no 
hit is found. Looking at LUR implementations they seems to be searching 
the cache for a hit before moving on the eviction part. I'm sure we 
could have two lists one hash map to lookup hot hits and a LUR to 
fallback to if no hit is found.

But I'm not sure LUR would be quicker or not, the cache sizes we have 
today are bellow 5 so not sure the extra complexity would pay of.

> 
> > Signed-off-by: Niklas Söderlund <niklas.soderlund@ragnatech.se>
> > ---
> >  src/libcamera/include/v4l2_videodevice.h | 1 +
> >  src/libcamera/v4l2_videodevice.cpp       | 9 ++++++---
> >  2 files changed, 7 insertions(+), 3 deletions(-)
> > 
> > diff --git a/src/libcamera/include/v4l2_videodevice.h b/src/libcamera/include/v4l2_videodevice.h
> > index fcf072641420dacf..f04feed87b24f28f 100644
> > --- a/src/libcamera/include/v4l2_videodevice.h
> > +++ b/src/libcamera/include/v4l2_videodevice.h
> > @@ -125,6 +125,7 @@ private:
> >  		bool operator==(const FrameBuffer &buffer);
> >  
> >  		bool free;
> > +		utils::time_point lastUsed;
> >  
> >  	private:
> >  		struct Plane {
> > diff --git a/src/libcamera/v4l2_videodevice.cpp b/src/libcamera/v4l2_videodevice.cpp
> > index 99470ce11421c77c..9a4d2479b20f5e27 100644
> > --- a/src/libcamera/v4l2_videodevice.cpp
> > +++ b/src/libcamera/v4l2_videodevice.cpp
> > @@ -205,6 +205,7 @@ int V4L2BufferCache::get(const FrameBuffer &buffer)
> >  {
> >  	bool hit = false;
> >  	int use = -1;
> > +	utils::time_point oldest = utils::clock::now();
> >  
> >  	for (unsigned int index = 0; index < cache_.size(); index++) {
> >  		const Entry &entry = cache_[index];
> > @@ -212,8 +213,10 @@ int V4L2BufferCache::get(const FrameBuffer &buffer)
> >  		if (!entry.free)
> >  			continue;
> >  
> > -		if (use < 0)
> > +		if (cache_[index].lastUsed < oldest) {
> >  			use = index;
> > +			oldest = cache_[index].lastUsed;
> > +		}
> >  
> >  		/* Try to find a cache hit by comparing the planes. */
> >  		if (cache_[index] == buffer) {
> > @@ -245,12 +248,12 @@ void V4L2BufferCache::put(unsigned int index)
> >  }
> >  
> >  V4L2BufferCache::Entry::Entry()
> > -	: free(true)
> > +	: free(true), lastUsed(utils::clock::now())
> >  {
> >  }
> >  
> >  V4L2BufferCache::Entry::Entry(bool free, const FrameBuffer &buffer)
> > -	: free(free)
> > +	: free(free), lastUsed(utils::clock::now())
> >  {
> >  	for (const FrameBuffer::Plane &plane : buffer.planes())
> >  		planes_.emplace_back(plane);
> 
> -- 
> Regards,
> 
> Laurent Pinchart
Naushir Patuck Feb. 17, 2020, 4:33 p.m. UTC | #3
Hi Niklas,


On Sun, 16 Feb 2020 at 16:17, Niklas Söderlund
<niklas.soderlund@ragnatech.se> wrote:
>
> 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 they cache was not used as good as it
> could for imported buffers.
>
> Improve this by adding a last usage timestamp 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>
> ---
>  src/libcamera/include/v4l2_videodevice.h | 1 +
>  src/libcamera/v4l2_videodevice.cpp       | 9 ++++++---
>  2 files changed, 7 insertions(+), 3 deletions(-)
>
> diff --git a/src/libcamera/include/v4l2_videodevice.h b/src/libcamera/include/v4l2_videodevice.h
> index fcf072641420dacf..f04feed87b24f28f 100644
> --- a/src/libcamera/include/v4l2_videodevice.h
> +++ b/src/libcamera/include/v4l2_videodevice.h
> @@ -125,6 +125,7 @@ private:
>                 bool operator==(const FrameBuffer &buffer);
>
>                 bool free;
> +               utils::time_point lastUsed;
>
>         private:
>                 struct Plane {
> diff --git a/src/libcamera/v4l2_videodevice.cpp b/src/libcamera/v4l2_videodevice.cpp
> index 99470ce11421c77c..9a4d2479b20f5e27 100644
> --- a/src/libcamera/v4l2_videodevice.cpp
> +++ b/src/libcamera/v4l2_videodevice.cpp
> @@ -205,6 +205,7 @@ int V4L2BufferCache::get(const FrameBuffer &buffer)
>  {
>         bool hit = false;
>         int use = -1;
> +       utils::time_point oldest = utils::clock::now();
>
>         for (unsigned int index = 0; index < cache_.size(); index++) {
>                 const Entry &entry = cache_[index];
> @@ -212,8 +213,10 @@ int V4L2BufferCache::get(const FrameBuffer &buffer)
>                 if (!entry.free)
>                         continue;
>
> -               if (use < 0)
> +               if (cache_[index].lastUsed < oldest) {
>                         use = index;
> +                       oldest = cache_[index].lastUsed;
> +               }
>
>                 /* Try to find a cache hit by comparing the planes. */
>                 if (cache_[index] == buffer) {
> @@ -245,12 +248,12 @@ void V4L2BufferCache::put(unsigned int index)
>  }
>
>  V4L2BufferCache::Entry::Entry()
> -       : free(true)
> +       : free(true), lastUsed(utils::clock::now())
>  {
>  }
>
>  V4L2BufferCache::Entry::Entry(bool free, const FrameBuffer &buffer)
> -       : free(free)
> +       : free(free), lastUsed(utils::clock::now())
>  {
>         for (const FrameBuffer::Plane &plane : buffer.planes())
>                 planes_.emplace_back(plane);

Thank you, this does fix my issue with cache entry under-utilisation.

Reviewed-by: Naushir Patuck <naush@raspberrypi.com>

> --
> 2.25.0
>
> _______________________________________________
> libcamera-devel mailing list
> libcamera-devel@lists.libcamera.org
> https://lists.libcamera.org/listinfo/libcamera-devel

Thanks,
Naush

Patch

diff --git a/src/libcamera/include/v4l2_videodevice.h b/src/libcamera/include/v4l2_videodevice.h
index fcf072641420dacf..f04feed87b24f28f 100644
--- a/src/libcamera/include/v4l2_videodevice.h
+++ b/src/libcamera/include/v4l2_videodevice.h
@@ -125,6 +125,7 @@  private:
 		bool operator==(const FrameBuffer &buffer);
 
 		bool free;
+		utils::time_point lastUsed;
 
 	private:
 		struct Plane {
diff --git a/src/libcamera/v4l2_videodevice.cpp b/src/libcamera/v4l2_videodevice.cpp
index 99470ce11421c77c..9a4d2479b20f5e27 100644
--- a/src/libcamera/v4l2_videodevice.cpp
+++ b/src/libcamera/v4l2_videodevice.cpp
@@ -205,6 +205,7 @@  int V4L2BufferCache::get(const FrameBuffer &buffer)
 {
 	bool hit = false;
 	int use = -1;
+	utils::time_point oldest = utils::clock::now();
 
 	for (unsigned int index = 0; index < cache_.size(); index++) {
 		const Entry &entry = cache_[index];
@@ -212,8 +213,10 @@  int V4L2BufferCache::get(const FrameBuffer &buffer)
 		if (!entry.free)
 			continue;
 
-		if (use < 0)
+		if (cache_[index].lastUsed < oldest) {
 			use = index;
+			oldest = cache_[index].lastUsed;
+		}
 
 		/* Try to find a cache hit by comparing the planes. */
 		if (cache_[index] == buffer) {
@@ -245,12 +248,12 @@  void V4L2BufferCache::put(unsigned int index)
 }
 
 V4L2BufferCache::Entry::Entry()
-	: free(true)
+	: free(true), lastUsed(utils::clock::now())
 {
 }
 
 V4L2BufferCache::Entry::Entry(bool free, const FrameBuffer &buffer)
-	: free(free)
+	: free(free), lastUsed(utils::clock::now())
 {
 	for (const FrameBuffer::Plane &plane : buffer.planes())
 		planes_.emplace_back(plane);