Reputation: 3125
I have an application where i am using boost::shared_mutex
locking to write a cv::Mat
in one thread, and then call it from many other threads.
this works well, but having all the reads is slowing the writer. This is, i imagine, because the write is waiting for all the reads to complete. Is there a different kind of boost
Lock that allows the writer to run at full speed, with many readers? Or do I need to find another way around this?
My relevant code is:
//writer.h
cv::Mat currentFrame;
typedef boost::shared_mutex Lock;
typedef boost::unique_lock< Lock > WriteLock;
Lock frameLock;
//writer.cpp
WriteLock w_lock(frameLock);
cv_img.copyTo(currentFrame);
frameLock.unlock();
//reader.h
typedef boost::shared_mutex Lock;
typedef boost::shared_lock< Lock > ReadLock;
//reader.cpp
cv::Mat frame;
ReadLock r_lockz(writer->frameLock);
writer->currentFrame.copyTo(frame);
_lockz.unlock();
Thank you.
Upvotes: 0
Views: 201
Reputation: 1688
Instead of copying, just assign it and create a new one as cv::Mat essentially a shared_ptr
//writer.h
cv::Mat currentFrame;
typedef boost::shared_mutex Lock;
typedef boost::unique_lock< Lock > WriteLock;
Lock frameLock;
//writer.cpp
WriteLock w_lock(frameLock);
currentFrame = cv_img;
frameLock.unlock();
//reader.h
typedef boost::shared_mutex Lock;
typedef boost::shared_lock< Lock > ReadLock;
//reader.cpp
cv::Mat frame;
ReadLock r_lockz(writer->frameLock);
frame = writer->currentFrame;
_lockz.unlock();
The cv_img is just a front for the current buffer in use. The readers will get a buffer that does not change mid read cycle(but maybe out of sync with the current frame until they finish) but the scope of their frame is what determines the life of each buffer.
By doing a deep copy you are creating a buffer in a shared_ptr(cv::Mat) and then doing full copies to each user of it instead of creating a immutable buffer that will go out of scope when everyone is done. The locking will keep the modification. On a side note, you may want to look at using a scoped_lock inside the critical section and putting braces or scope around it. that way if you throw or forget to unlock it doesn't break everything.
{
boost::scoped_lock(frameLock);
currentFrame = cv_img;
}
If you are having a lot of contention and it is the lock time(locks without contention are not "that" bad) a lock free queue might work
#include <boost/lockfree/spsc_queue.hpp>
// writer.h
using cv_queue_t = boost::lockfree::spsc_queue<cv::Mat>// cv::Mat might need this to be wrapped for contructor requirements
cv_queue_t cv_queue;
// writer.cpp
while( !cv_queue.push( cv_img ) ) { // could put a sleep in here }
// reader.cpp
while( !writer->cv_queue.pop( frame ) ) { // could put sleep in here }
Upvotes: 0
Reputation: 85266
You can try double-buffering, i.e. use two cv:Mat
(frame) instances and alternate between them.
Write into the "inactive" frame (sequentially) while reading from the "active" frame (can be parallel). After a new frame is written, designate it as "active" and the other as "inactive".
Shared state:
cv::Mat currentFrame[2];
std::shared_mutex frameMutex[2];
std::mutex writeMutex;
std::atomic<int> activeFrame = 0;
Writer implementation:
std::unique_lock<std::mutex> writeLock(writeMutex);
const int writeFrame = activeFrame ^ 1;
std::unique_lock<std::shared_mutex> frameLock(frameMutex[writeFrame]);
cv_img.copyTo(currentFrame[writeFrame]);
activeFrame = writeFrame;
Reader implementation:
const int readFrame = activeFrame;
std::shared_lock<std::shared_mutex> frameLock(frameMutex[readFrame]); // lock shared
currentFrame[readFrame].copyTo(frame);
The assumption is that reading is significantly faster than writing. Otherwise there will still be some lock contention.
It might be a good idea to refactor the code a bit and extract currentFrame (and associated locks) from the "Writer" class into a separate shared "State" class.
Upvotes: 0
Reputation: 977
The whole point of mutual exclusions here is to prevent read/write collisions. The slow-down is exactly what you'd expect when a slower process is locking a resource.
The bottleneck will always exist here with your current approach; you need to find another. Perhaps concurrency of a different sort (multiple readers/writers?).
Upvotes: 1