如何有效地将 std::vector 视为 C 缓冲区
How to treat a std::vector like a C buffer efficiently?
使用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);
};
- 有关插入适配器的错误。[错误]请求从 'back_insert_iterator<vector<>>' 类型转换为非标量类型
- 在c++中用vector填充一个简单的动态数组
- vector.resize()中的分配错误
- 使用std::vector的OpenCL矩阵乘法
- POCO::PostgreSQL:如何将std::vector支持添加到`Binder::bind`
- 在某些循环内使用vector.push_back时出现分段错误
- 当vector是tje全局变量时,c++中vector的内存管理
- std::vector的包装器,使数组的结构看起来像结构的数组
- 为什么(-1)%vector::size()总是返回0
- 在C++中将类(带有Vector成员)保存为二进制文件
- 编译器如何区分std::vector的构造函数
- 将 int 数组转换为 std::vector<int*>
- 使用 pqxx 将 std::vector 存储在 postgresql 中,并从数据库中检索它
- 在std::vector上存储带有模板的类实例
- 在main()之外初始化std::vector会导致性能下降(多线程)
- 为什么std::vector比数组慢
- std::vector::迭代器是否可以合法地作为指针
- 如何将二进制格式的 C++ 对象的 std::vector 保存到磁盘?
- 将 vector<int*> 视为 vector<const int*>而不复制 (C++0x)
- 如何有效地将 std::vector 视为 C 缓冲区