适用于大型阵列的无拷贝线程安全环形缓冲区

lux*_*fux 5 c++ vector circular-buffer lock-free move-semantics

对于大型数组(10 ^ 7个元素)上的信号处理,我使用与环形缓冲区连接的不同线程。可悲的是,将数据复制到缓冲区或从缓冲区复制数据只需要太多时间。当前的实现基于boost::lockfree::spsc_queue

因此,我正在寻找一种解决方案,以通过在向量和向量之间使用unique_ptr来在向量和线程之间交换向量的所有权(请参见附图:在线程和队列之间交换指针)。

移动智能指针不符合我的需求,因为因此我需要在运行时不断为新的矢量元素分配内存。该开销大于复制数据的开销。

我在设计上缺少缺陷吗?

是否有线程安全或什至无锁的环形缓冲区实现允许对push和pop进行交换操作?

编辑:我修改了一个锁环缓冲区以交换unique_ptr。性能提升巨大。虽然这并不是一个优雅的解决方案。有什么建议吗?

// https://github.com/embeddedartistry/embedded-resources/blob/master/examples/cpp/circular_buffer.cpp

#include <memory>
#include <mutex>

template <typename T, int SIZE>
class RingbufferPointer {
typedef std::unique_ptr<T> TPointer;
public:
    explicit RingbufferPointer() {
        // create objects
        for (int i=0; i<SIZE; i++) {
            buf_[i] = std::make_unique<T>();
        }
    }

    bool push(TPointer &item) {
        std::lock_guard<std::mutex> lock(mutex_);
        if (full())
            return false;

        std::swap(buf_[head_], item);

        if (full_)
            tail_ = (tail_ + 1) % max_size_;

        head_ = (head_ + 1) % max_size_;
        full_ = head_ == tail_;

        return true;
    }

    bool pop(TPointer &item) {
        std::lock_guard<std::mutex> lock(mutex_);
        if (empty())
            return false;

        std::swap(buf_[tail_], item);

        full_ = false;
        tail_ = (tail_ + 1) % max_size_;

        return true;
    }

    void reset() {
        std::lock_guard<std::mutex> lock(mutex_);
        head_ = tail_;
        full_ = false;
    }

    bool empty() const {
        return (!full_ && (head_ == tail_));
    }

    bool full() const {
        return full_;
    }

    int capacity() const {
        return max_size_;
    }

    int size() const {
        int size = max_size_;

        if(!full_) {
            if(head_ >= tail_)
                size = head_ - tail_;
            else
                size = max_size_ + head_ - tail_;
        }

        return size;
    }

private:
    TPointer buf_[SIZE];

    std::mutex mutex_;
    int head_ = 0;
    int tail_ = 0;
    const int max_size_ = SIZE;
    bool full_ = 0;
};
Run Code Online (Sandbox Code Playgroud)

Rei*_*ica 1

移动智能指针不符合我的需求,因为因此我需要在运行时不断为新的向量元素分配内存。

如果您预先分配足够的存储并实现自己的内存管理(简单的隔离存储,又称池化) ,则不一定正确。

如果您这样做,就没有什么可以阻止您进行交换,并且您可以使用任何支持元素交换的环形缓冲区来保持现有的体系结构,并保持与之前相同的线程安全性。您可以检查仅使用boost::pool而不是实现您自己的选项。