boost::circular_buffer 性能对比及用法小结

最近在项目中用到了循环队列的语义,于是引进了boost库的circular_buffer.

从直观了解,circular_buffer是预分配整块连续内存,并通过维护其首尾指针来实现循环,大部分情况下,性能应该和vector是一致的, 只有在first指针发生翻转时,会出现性能的损失。

来自Kurt Guntherothcircular_buffer和vector, deque, list的性能比对 更加清晰的展现了其性能的比对情况,推荐阅读。

这里引用其中的一些比对结果数据:

操作circular_buffervectordequelist
assign15341162102379389
assign part1383105073126724
delete part15111229252665
insert(end())1004107472346493
insert(end()), with reserve-1032--
insert(begin())91478494084888131956745
push_back()997472468146635
push_back(), with reserve-1412--
push_front()1212-67386641
容器遍历(iterator)154158554388
sort()3337.92335.82968.53333.5
sort() sorted954.9420.8460.51301.5
search container358024523729-
push/pop as queue1155-18488463

从上面的数据,我们可以看出,circular_buffer在大部分操作上和vector性能接近, 大部分场景下,使用circular_buffer优于deque和list.

同时,我们可以看到,往circular_buffer数据中插入一个数据是耗费很大的操作, 因为它需要对插入位置后的所有数据做一次移动的操作。 所以,如果有插入数据的情景,应该避免使用circular_buffer.

std::copy的效率

针对circular_buffer复制的效率,我通过test_circular_buffer.cpp进行了测试, 比较了直接使用array_one进行复制以及使用迭代器(begin(), end())进行复制的效率,结果如下:

4M数据拷贝100次,取最小值,最大值,和平均值,这里单位为微秒(micro seconds)

操作最小值最大值均值
copy with c array cost1811438304
copy with iterator cost217542612435

从上面测试结果可以看到,使用array_one提供的连续内存拷贝会更快,应该是编译器识别到其是连续内存,而采取了优化措施。 所以,在进行块拷贝时,我们还是需要使用array_one(), array_two()进行复制。

circular_buffer使用心得

  • 只使用push语义,即往尾部插入数据
  • 在拷贝一段数据时,推荐转换成c风格的数组后,再通过std::copy拷贝
  • circular_buffer默认是固定长度,在满后,会覆盖尾部指针,所以,在插入数据前,需要确定数据有空间,避免数据被覆盖。