如何有效地将 std::vector 视为 C 缓冲区

How to treat a std::vector like a C buffer efficiently?

本文关键字:vector 视为 缓冲区 std 有效地      更新时间:2023-10-16

使用C缓冲区,我经常这样做:

BYTE buffer[MAX_SIZE];
int dataSize = 0;
while (appRunning()) 
{
    dataSize += Receive(buffer + dataSize, MAX_SIZE - dataSize);
    int processedSize = ProcessBuffer(buffer, dataSize);
    ASSERT(processedSize <= dataSize);
    dataSize -= processedSize;
    memmove(buffer, buffer + processedSize, dataSize);
};

是否可以在不损失太多性能的情况下使用std::vector做到这一点?

编辑:我找到了一种用std::vector替换原始 C 缓冲区的方法。

std::vector<BYTE> vbuf;
vbuf.reserve(MAX_SIZE); // allocated at once
while (appRunning()) 
{
    int pendingSize = GetPendingDataSize(); // from a socket
    if (pendingSize > vbuf.capacity())
        pendingSize = vbuf.capacity();
    vbuf.resize(pendingSize);
    int recvSize = Receive(vbuf.data(), vbuf.size());
    ASSERT(recvSize < vbuf.size());
    int processedSize = ProcessBuffer(vbuf.data(), vbuf.size());
    std::rotate(vbuf.begin(), vbuf.begin() + processedSize, vbuf.end());
    vbuf.resize(vbuf.size() - processedSize);
};

实际上,在我的实际使用中,接收数据和处理数据可以在多线程中完成。因此,通过使用 vector,我不需要手动管理缓冲区的分配、数据大小和缓冲区容量。与 C 缓冲区相比,此处的性能损失是在调用vbuf.resize()时。但我认为惩罚是微不足道的。任何更好的方法都是值得赞赏的。

通过TCP连接接收消息时,缓冲区中的最后一条消息可能不完整。处理完完整的消息后,人们通常只是将最后一条不完整的消息memmove到缓冲区的开头。

另一种策略是使用"智能"环形缓冲区来避免这种memmove,并避免数据包装在环形缓冲区上,从而产生不连续性。要使"智能"环形缓冲区使用mmap为缓冲区分配内存,并映射页面的同一区域两次,中间没有间隙。这样,超过缓冲区末尾的读取将继续从头开始读取它,从而防止使用常规环形缓冲区时固有的不连续性。


对网络缓冲区使用 std::vector 不太理想,因为调整矢量大小会初始化其元素,这些元素稍后会被recv调用覆盖。对于此类缓冲区,不需要该初始化。

自 C++11 以来,C 数组的大部分都被 std::array 取代:

std::array<BYTE, MAX_SIZE> buffer;

它只是围绕 C 数组的薄包装器。因此,从性能的角度来看,您不会丢失任何东西。但是,有更好的合作方式。

但与 C 数组一样,它们仅适用于在编译时知道大小的固定时间数组。无法调整它们的大小。但是,如果我正确阅读了您的问题,那么您就不需要额外的灵活性。

完全

可以用 std::vector 或等效的 std::array 替换它。您不需要调整矢量大小,所以不要。

std::vector<BYTE> buffer(MAX_SIZE);
BYTE * start = buffer.data();
int dataSize = 0;
while (appRunning()) 
{
    dataSize += Receive(start, MAX_SIZE - dataSize);
    int processedSize = ProcessBuffer(buffer.data(), dataSize);
    ASSERT(processedSize <= dataSize);
    dataSize -= processedSize;
    start = std::copy_n(buffer.data() + processedSize, dataSize, buffer.data());
};

如果你想要类似于C的行为(也就是说,你想保证向量永远不会释放或为底层向量分配更多的内存),一个不错的解决方案是使用Boost的static_vector。它静态分配基础缓冲区,但除此之外,它的行为类似于普通向量。

boost::static_vector<BYTE, MAX_SIZE> buffer;

但是,对于这种类型的活动,您也可以查看std::queue或boost::cirular_buffer,看看其中之一是否符合您的需求。

从内存中写入,请检查它是否适合您:

std::vector<BYTE> buffer;
buffer.reserve(MAX_SIZE);
int dataSize = 0;
while (appRunning()) 
{
    dataSize += Receive(&buffer[0] + dataSize, MAX_SIZE - dataSize);
    int processedSize = ProcessBuffer(&buffer[0], dataSize);
    ASSERT(processedSize <= dataSize);
    dataSize -= processedSize;
    memmove(&buffer[0], &buffer[0] + processedSize, dataSize);
};