c++ - 用于大数组的无复制线程安全环形缓冲区

标签 c++ vector move-semantics lock-free circular-buffer

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

因此,我正在寻找一种解决方案,通过对 vector 使用 unique_ptr 在线程和缓冲区之间交换 vector 的所有权(请参阅附图: swapping pointer between threads and the queue )。

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

我是否遗漏了该设计中的缺陷?

是否有线程安全甚至无锁的环形缓冲区实现允许推送和弹出交换操作?

编辑:我修改了一个锁定环缓冲区来交换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;
};

最佳答案

Moving smart pointers doesn't fit my needs, because therefore I need to allocate memory during runtime constantly for new vector elements.

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

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

关于c++ - 用于大数组的无复制线程安全环形缓冲区,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52201212/

相关文章:

c++ - 方法不会重载

c++ - 如何删除空指针?

c++ - `noncopyable` 带有自定义析构函数

c++ - 将类的 move 成员作为const引用参数传递

c++ - 如何在没有终端窗口的情况下启动 mac 应用程序

c++ - extern "C"的含义

c++如何将文件解析为结构 vector

c++ - 在 C++ 中删除 vector 中的第一个最小数字(并保持顺序)

c++ - std::vector push_back() 删除前一个元素

c++ - 多个构造函数参数的 move 语义