C++:查找程序中最大的容器

c++: Find largest container in a program

本文关键字:查找 程序 C++      更新时间:2023-10-16

我正在尝试分析一个大型C++程序。该程序大量使用 STL 容器数据结构,如集合、映射、无序集合、无序映射、向量等。有时它们是嵌套的,例如集合映射。

我想找出,在程序的特定运行中,哪些容器包含最多的元素(即size((的最大值(。我可以对程序进行小的编辑。

如果有一种方法可以

遍历所有容器,或者有一种方法可以拦截容器的(大小修改(API,这可能会有所帮助。但这些是不可能的。

你会如何处理这个问题?

补充:Linux中的平台,编译器是g++或clang++。

如果您可以进行较小的编辑,您可以将每个容器添加到它们的大列表中吗?
喜欢这个:

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
    }
  }
};

当您的项目非常大并且具有大量不同容器的实例时,此方法很有用。该方法的优点是不需要修改大量代码。 它允许您缩小要查找的容器类型。此方法有助于诊断每个条件和每个类型的位置。

可以重新定义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>,

所以你可以知道 3 个元素在vector<A> A,2 个元素A map<string,A>,2 个元素Amap<string,A>

将统计信息代码添加到 std 头文件中容器的析构函数中。这也不需要修改大项目的大量代码。但这也只显示容器类型(请参阅我的另一个答案 这里(.方法不需要 C++0x 或 C++11 或更多。

第一步也是强制性步骤是在源代码管理(例如 git(下添加您的 std 库,以便快速查看实际更改的内容以及在修改版本和原始版本之间快速切换。

Stat类的声明放入 std 库源文件夹中:

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;
}

编辑标准库容器模板。在析构函数中添加统计记录。

// 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