我需要将排序后的元素连续存储在内存中,所以我想到了 std::vector 和 boost::flat_set。我都试过了,我检查了它们的性能,虽然使用 std::vector 后插入要快一点,但使用 boost::flat_set 前插入要快得多。这是我的测试代码:
#include <iostream>
#include <vector>
#include <boost/container/flat_set.hpp>
#include <boost/chrono.hpp>
#include <windows.h>
// Just a basic movable object for my tests
class Component
{
public :
Component( void ) :
id( 0 ),
name( "default" ),
data( 100 )
{
}
Component( uint32_t id ) :
id( id ),
name( "default" ),
data( 100 )
{
}
Component( Component&& component ) throw() :
id( std::move( component.id ) ),
name( std::move( component.name ) ),
data( std::move( component.data ) )
{
}
Component& operator=( Component&& component ) throw()
{
id = std::move( component.id );
name = std::move( component.name );
data = std::move( component.data );
return ( *this );
}
uint32_t get_id( void ) const
{
return ( id );
}
private :
uint32_t id;
std::string name;
std::vector< uint32_t > data;
};
// This object can be sorted
inline bool operator<( const Component& component1, const Component& component2 )
{
return ( component1.get_id() < component2.get_id() );
}
#define COMP_NB 1000000
int main( void )
{
/*******************************/
/* Test vector insertion speed */
/*******************************/
std::vector< Component > vector;
vector.reserve( COMP_NB + 1 );
std::cout << "Push back components in the vector: ";
auto startTime = boost::chrono::steady_clock::now();
// Back insertion
for ( uint32_t i = 0; i < COMP_NB; ++i )
{
vector.push_back( Component( i + 1 ) );
}
auto thisTime = boost::chrono::steady_clock::now();
std::cout << boost::chrono::duration_cast< boost::chrono::milliseconds >( thisTime - startTime ).count() << "ms" << std::endl;
std::cout << "Insert one component at the beginning of the vector: ";
startTime = boost::chrono::steady_clock::now();
// Front insertion (all components are shifted)
vector.insert( vector.begin(), Component( 0 ) );
thisTime = boost::chrono::steady_clock::now();
std::cout << boost::chrono::duration_cast< boost::chrono::milliseconds >( thisTime - startTime ).count() << "ms" << std::endl;
/*********************************/
/* Test flat_set insertion speed */
/*********************************/
boost::container::flat_set< Component > flat_set;
flat_set.reserve( COMP_NB + 1 );
std::cout << "Push back components in the flat_set: ";
startTime = boost::chrono::steady_clock::now();
// Back insertion
for ( uint32_t i = 0; i < COMP_NB; ++i )
{
flat_set.insert( Component( i + 1 ) );
}
thisTime = boost::chrono::steady_clock::now();
std::cout << boost::chrono::duration_cast< boost::chrono::milliseconds >( thisTime - startTime ).count() << "ms" << std::endl;
std::cout << "Insert one component at the beginning of the flat_set: ";
startTime = boost::chrono::steady_clock::now();
// Front insertion (all components are shifted)
flat_set.insert( Component( 0 ) );
thisTime = boost::chrono::steady_clock::now();
std::cout << boost::chrono::duration_cast< boost::chrono::milliseconds >( thisTime - startTime ).count() << "ms" << std::endl;
system( "PAUSE" );
return ( 0 );
}
和输出:
推回vector中的分量:852ms 在vector开头
插入一个分量:59ms
推回flat_set中的分量:912ms 在flat_set开头
插入一个分量:16ms
使用 flat_set 前插入速度提高了 3.6 倍!所以我又进行了一次测试,因为我想看看我的移动功能是否被使用,我发现了一些奇怪的东西。这是新代码:
#include <iostream>
#include <vector>
#include <boost/container/flat_set.hpp>
#include <boost/chrono.hpp>
#include <windows.h>
// Just a basic movable object for my tests
class Component
{
public :
Component( void ) :
id( 0 ),
name( "default" ),
data( 100 )
{
std::cout << "Default constructor" << std::endl;
}
Component( uint32_t id ) :
id( id ),
name( "default" ),
data( 100 )
{
std::cout << "Custom constructor" << std::endl;
}
Component( Component&& component ) throw() :
id( std::move( component.id ) ),
name( std::move( component.name ) ),
data( std::move( component.data ) )
{
std::cout << "Move constructor" << std::endl;
}
Component& operator=( Component&& component ) throw()
{
std::cout << "Move assignment operator" << std::endl;
id = std::move( component.id );
name = std::move( component.name );
data = std::move( component.data );
return ( *this );
}
uint32_t get_id( void ) const
{
return ( id );
}
private :
uint32_t id;
std::string name;
std::vector< uint32_t > data;
};
// This object can be sorted
inline bool operator<( const Component& component1, const Component& component2 )
{
return ( component1.get_id() < component2.get_id() );
}
#define COMP_NB 1
int main( void )
{
/*******************************/
/* Test vector insertion speed */
/*******************************/
std::vector< Component > vector;
vector.reserve( COMP_NB + 1 );
std::cout << "-- Push back one component in the vector: " << std::endl;
// Back insertion
for ( uint32_t i = 0; i < COMP_NB; ++i )
{
vector.push_back( Component( i + 1 ) );
}
std::cout << "-- Insert one component at the beginning of the vector: " << std::endl;
// Front insertion (the other component is shifted)
vector.insert( vector.begin(), Component( 0 ) );
std::cout << std::endl;
/*********************************/
/* Test flat_set insertion speed */
/*********************************/
boost::container::flat_set< Component > flat_set;
flat_set.reserve( COMP_NB + 1 );
std::cout << "-- Push back one component in the flat_set: " << std::endl;
// Back insertion
for ( uint32_t i = 0; i < COMP_NB; ++i )
{
flat_set.insert( Component( i + 1 ) );
}
std::cout << "-- Insert one component at the beginning of the flat_set: " << std::endl;
// Front insertion (the other component is shifted)
flat_set.insert( Component( 0 ) );
system( "PAUSE" );
return ( 0 );
}
新输出:
-- 推回向量中的一个组件:
自定义构造函数
移动构造函数
--在向量开头插入一个组件:
自定义构造
函数移动构造函数移动构造
函数
移动赋值运算符
移动赋值运算符
--推回 flat_set 中的一个组件:
自定义构造函数
移动构造函数
——在flat_set开头插入一个组件:
自定义构造函数
移动构造函数
移动赋值运算符
这里有一些奇怪的东西。Flat_set 行为似乎很正常:
-- 在flat_set开头插入一个组件:
自定义构造函数 //Ok, 我要求创建一个新组件
Move构造函数 //Ok, flat_set需要将前一个组件移到一个新位置
Move assignment operator //OK , 新组件需要移动到 flat_set 的前面
但是向量呢?
-- 在向量的开头插入一个组件:
自定义构造函数 // 好的,我要求创建一个新的组件
移动构造函数 // 好的,向量需要将前一个组件移到一个新的位置
移动构造函数 // 等等。 .. 什么?
移动赋值运算符 //OK,新组件需要移动到向量前面
移动赋值运算符 //Wtf?
我尝试了更多组件,并且矢量行为是相同的:它继续执行所有移动操作两次。为什么?可以避免吗?如果没有,我应该坚持 flat_set (我有时需要转移我的数据)?
[编辑]:这是输出,其中 10 个组件被插入到后面,1 个组件被插入到前面,并且组件的 id 正在构建或移动: http: //pastebin.com/SzT5M8yP
[编辑 2]:我用 boost::container::vector 做了同样的测试,输出(和速度!)与 flag_set 相同。微软实现vector有问题吗?哦
[编辑 3]:提交给 Microsoft 的问题:https ://connect.microsoft.com/VisualStudio/feedback/details/801205/std-vector-do-extra-operations-when-shifting-elements