我正在尝试分析一个大型的C ++程序。该程序大量使用STL容器数据结构,如set,map,unordered set,unordered map,vector等。有时它们是嵌套的,例如集合图。
我想在程序的特定运行中找出哪些容器拥有最多数量的元素(即size()的最大值)。我可以对该程序进行少量编辑。
如果有办法迭代所有容器,或者有方法拦截容器的(大小修改)API,那可能会有所帮助。但这些都是不可能的。
你会怎么做?
另外:Linux中的平台,编译器是g ++或clang ++。
答案 0 :(得分:2)
当您的项目非常大并且具有不同容器的实例时,此方法很有用。方法的优点是您不需要修改大量代码。它允许您缩小要查找的容器类型。这种方法有助于诊断每个容器和每种类型的情况。
可以重新定义template< class T > struct allocator
。可以在std头中重命名原始分配器或修改它。可以对分配和释放进行统计。您将了解每种元素的数量和大小。但是你不知道哪个容器实例有元素。
模板template< class T > struct allocator
放置在库头文件中。它始终存在,并且不需要重建您的开发环境库,因为据您所知,模板无法编译到静态库中(排除特殊化)。始终使用您的来源编译的模板。但是预编译头文件可能有问题。对于项目,可以重新生成或不使用它,但对于库,需要检查。这可能是方法的瓶颈,但验证存在问题很简单。
有一种经验方法不能保证准确性。当您的应用程序关闭时,容器在其元素解除分配后解除分配。因此,您可以根据父类型的容器编写统计信息,内部元素在哪种类型的容器中有多少。
例如,我们有:
vector<A>({1,2,3}) and map<string,B>({1,2}) and map<string,B>({1,2})
这将生成如下的释放事件列表:
B, B, map<string,B>,
A, A, map<string,A>,
A, A, A, vector<A>,
因此,您可以知道A
处的3个元素vector<A>
,A
处的2个元素map<string,A>
以及A
处的2个元素map<string,A>
答案 1 :(得分:2)
如果您可以进行少量编辑,是否可以将每个容器添加到它们的大列表中?
像这样:
std::set<......> my_set; // existing code
all_containers.add( &my_set ); // minor edit IMHO
然后,您可以致电all_containers.analyse()
,在每个人身上调用size()
并打印结果。
你可以使用类似的东西:
struct ContainerStatsI {
virtual int getSize() = 0;
};
template<class T> struct ContainerStats : ContainerStatsI {
T* p_;
ContainerStats( T* p ) : p_(p) {}
int getSize() { return p->size(); }
};
struct ContainerStatsList {
std::list<ContainerStatsI*> list_; // or some other container....
template<class T> void add( T* p ) {
list_.push_back( new ContainerStats<T>(p) );
}
// you should probably add a remove(T* p) as well
void analyse() {
for( ContainerStatsI* p : list_ ) {
p->getSize(); // do something with what's returned here
}
}
};
答案 2 :(得分:1)
将统计信息代码添加到std头文件中的容器析构函数中。这也不需要修改大项目的大量代码。但是这只显示了容器类型(请参阅我的另一个答案)。方法不需要C ++ 0x或C ++ 11或更多。
第一个和强制步骤是在源代码管理下添加您的std库,例如git,以便快速查看实际更改的内容以及在修改后的原始内容之间快速切换版本
将此Stat
类声明放入std library sources文件夹:
class Stat {
std::map<std::string,int> total;
std::map<std::string,int> maximum;
public:
template<class T>
int log( std::string cont, size_t size ) {
std::string key = cont + ": " + typeid(T).name();
if( maximum[key] < size ) maximum[key] = size;
total[key] += size;
}
void show_result() {
std::cout << "container type total maximum" << std::endl;
std::map<std::string,int>::const_iterator it;
for( it = total.begin(); it != total.end(); ++it ) {
std::cout << it->first << " " << it->second
<< " " << maximum[it->first] << std::endl;
}
}
static Stat& instance();
~Stat(){ show_result(); }
};
在项目cpp文件中实例化Stat
类的单例实例:
Stat& Stat::instance() {
static Stat stat;
return stat;
}
编辑std库容器模板。在析构函数中添加统计记录。
// modify this standart template library sources:
template< T, Allocator = std::allocator<T> > vector {
...
virtual ~vector() {
Stat::instance().log<value_type>( "std::vector", this->size() );
}
};
template< Key, T, Compare = std::less<Key>,
Allocator = std::allocator<std::pair<const Key, T> > map {
...
virtual ~map(){
Stat::instance().log<value_type>( "std::map", this->size() );
}
};
现在考虑一个程序:
int main() {
{
// reject to use C++0x, project does not need such dependency
std_vector<int> v1; for(int i=0;i<10;++i) v1.push_back( i );
std_vector<int> v2; for(int i=0;i<10;++i) v2.push_back( i );
std_map<int,std::string> m1; for(int i=0;i<10;++i) m1[i]="";
std_map<int,std::string> m2; for(int i=0;i<20;++i) m2[i]="";
}
Stat::instance().show_result();
return 0;
}
gcc的结果是:
container type total maximum std::map: St4pairIiSsE 30 20 std::vector: i 20 10
如果您需要更详细的类型描述,而不是查找有关您的开发环境的信息。此处描述的转换为gcc:https://lists.gnu.org/archive/html/help-gplusplus/2009-02/msg00006.html
输出可能是这样的:
container type total maximum std::map: std::pair<int, std::string> 30 20 std::vector: int 20 10