如何跟踪通话统计信息? C++

Posted

技术标签:

【中文标题】如何跟踪通话统计信息? C++【英文标题】:How to keep track of call statistics? C++ 【发布时间】:2012-07-02 21:14:19 【问题描述】:

我正在开发一个向用户提供统计信息的项目。我创建了一个名为 Dog 的类, 它有几个功能。说话、呼呼、奔跑、取物等。

我希望有一个函数可以显示每个函数被调用了多少次。我也对构造函数调用和析构函数调用感兴趣。

我有一个定义所有函数的头文件,然后是一个单独的 .cc 文件来实现它们。我的问题是,有没有办法跟踪每个函数被调用的次数?

我有一个名为 print 的函数,它将获取“统计信息”,然后将它们输出到标准输出。我正在考虑使用静态整数作为类本身的一部分,声明几个整数来跟踪这些事情。我知道编译器将创建整数的副本并将其初始化为最小值,然后我将在 .cc 函数中递增整数。

我还考虑过将静态整数作为 .cc 中的全局变量。哪种方式更容易?或者有更好的方法吗?

非常感谢任何帮助!

【问题讨论】:

您可以让局部变量在每次调用方法时递增。 【参考方案1】:

使用静态成员变量是要走的路。但是,编译器不会“创建整数的副本并将其初始化为最小值”;您必须为 .cc 文件中的每一个提供一个定义,并在那里将其初始化为 0。 (如果您使用的是 C++11,情况会有所不同,但基本思想是相同的。)

没有理由使用静态全局变量代替静态成员。

foo.h:

class Foo 
  static int countCtor_;
  static int countDtor_;
  static int countprint_:
  Foo();
  ~Foo();
  static void print();
;

foo.cc:

#include <iostream>
#include "foo.h"

int Foo::countCtor_ = 0;
int Foo::countDtor_ = 0;
int Foo::countprint_ = 0;

Foo::Foo() 
  ++countCtor_;
  // Something here

Foo::~Foo() 
  ++countDtor_;
  // Something here

void Foo::print() 
  ++countprint_;
  std::cout << "Ctor:  " << countCtor_ << "\n"
            << "Dtor:  " << countDtor_ << "\n"
            << "print: " << countprint_ << "\n";

但是,如果你有很多函数,所涉及的重复有点烦人——当你的意思是 ++countBaz_ 时,很容易意外地执行 ++countBar_(尤其是如果你复制和粘贴样板文件),所以你可能需要一些更花哨的东西,例如静态映射和递增计数[__FUNC__]的宏,因此您可以在每个函数中使用完全相同的行。像这样:

foo.h:

#include <map>
class Foo 
  static std::map<const char*, int> counts_;
  Foo();
  ~Foo();
  void print();
;

foo.cc:

#include <iostream>
#include "foo.h"

std::map<const char *, int> Foo::counts_;

#define INC_COUNT_() do  ++counts_[__FUNC__];  while (0)

Foo::Foo() 
  INC_COUNT_();
  // Something here

Foo::~Foo() 
  INC_COUNT_();
  // Something here

void Foo::print() 
  INC_COUNT_();
  for (std::map<const char *, int>::const_iterator it = counts_.begin(); 
       it != counts_.end(); ++it) 
    std::cout << it->first << ": " << it->second << "\n";
  

在上面的示例代码中,__FUNC__ 是一个占位符。不幸的是,没有可以使用的符合标准的值来代替它。大多数编译器都有一些 __func__、__FUNC__、__FUNCTION__、__FUNCSIG__ 和 __PRETTY_FUNCTION__ 的子集。但是,这些都不是 C++03 中的标准。 C++11 确实将 __func__ 标准化,但只是作为“实现定义的字符串”,不能保证它是有用的,甚至是唯一的。最重要的是,不同编译器的值会有所不同。此外,其中一些可能是宏而不是标识符,以使事情变得更有趣。

如果你想要真正可移植的代码,在 C++11 中,你可以使用类似 string(__func__) + ":" + STRINGIZE(__LINE__) 的东西——这有点难看,但至少每个函数都有一个唯一的姓名。而在 C++03 中,没有等价物。如果您只需要“足够便携”,请查阅您使用的每个编译器的文档,或者依赖 autoconf 之类的东西。

【讨论】:

我不明白为什么第一个 foo.cc 的格式被搞砸了。我已经尝试在每个空行中使用四个空格,并且使用实际的空行,无论哪种方式,有些行都不会显示为代码...... 我不知道__FUNC__,但我假设您的意思是标准__func__。但是,您应该真正使用函数的修饰名称,以便单独计算重载的函数名称。 对不起,你是对的;仅仅因为某些东西既是 MSVC 扩展又是 gcc 扩展并不意味着它是标准的。我会编辑答案。 非常感谢您的回复!很高兴看到我得到了正确的静态成员变量部分,即使编译器错误。 :) 你知道了解更多关于静态成员的好资源吗? 如果您不确定自己是否已经了解基础知识,任何好的 C++ 教程或教科书都应该能解释清楚。如果您想知道定义的确切细节,我不知道比标准更好的东西(很难阅读);也许其他人可以提供帮助。然而,Stroustrop 的两本书可能解释了古代 C++ 的事情,而从那时起静态成员的情况并没有太大变化。【参考方案2】:

您是否有任何理由不能使用标准分析工具来计算这些调用?像gprof 这样的东西?

否则静态整数将是要走的路。

【讨论】:

这个答案的前半部分很好,但后半部分不好。将静态整数放入方法本身使他无法创建他描述的“打印”函数(或任何其他访问值的方式,在调试器之外)。【参考方案3】:

假设您希望在程序中一直跟踪这些统计信息,您可以使用函数名称的 unordered_map

std::unordered_map<const char *, unsigned> stats;

void foo () 
    // use __FUNCDNAME__ for MSVC
    ++stats[__PRETTY_FUNCTION__];
    //...

使用编译器特定的函数名称说明符是有目的地获得修饰的函数名称。这样重载的函数名就会被算作单独的函数。

这种技术可以让您轻松添加新函数而无需考虑其他任何事情,但是如果存在哈希冲突(可以通过将stats 映射更大一些来弥补),则会产生少量额外成本。没有对字符串计算哈希,因为键是指针类型,它只是使用指针值本身作为哈希。

如果这只是用于分析的一次性代码,那么您应该首先尝试使用您平台上可用的代码分析工具。

【讨论】:

+1。尽管对微优化效率的关注似乎有点误导,但它仍然比我非常相似的答案更简单、更容易阅读。 @abarnert:感谢您的支持。如果这个问题的目的只是一个信息收集练习,那么我同意效率不应该是一个问题。但是,在对性能敏感的应用程序中,如果解决方案既干净又高效,那就太好了。问候 实际上,对于预期的 N 的微小值,我怀疑 map 很有可能比 unordered_map 更有效:log 10 可能比散列字符串更快,并且 3*10 可能小于哈希表的默认起始大小。它的读写速度也更快——尤其是当我看到 unordered_map 时,我想知道为什么选择它而不是 map。此外,它在编译器、运行和对代码的微小更改中将更加可预测。而且,最重要的是,它适用于 C++98。 @abarnert:字符串没有散列,指针值本身用作散列。唯一的计算是 unordered_map 中的桶数的模数。唯一会减速的时候是发生碰撞,如果函数的数量像你说的那么小,就不会发生这种情况。 老实说,如果不对每个编译器进行测量,我不会在不测量哪个更快或更小的情况下进行预测。但除非它有所作为,否则我不会费心去测量;我会把 C++98 的兼容性、可预测性、甚至简洁性放在性能之前。当然,如果他的类中有 5000 个方法……那么,他需要重构类,并且可能编写代码生成器而不是依赖 cpp……【参考方案4】:

您可以将 static 本地变量放在方法本身中,这看起来更简洁,因为这些变量在逻辑上没有连接到类,因此没有理由让它们成为成员。

此外,您可以使用宏来简化工作。我通常不建议使用宏,但这似乎是一个合适的用途:

#define DEFINE_COUNTER \
   static int noCalls = 0; \
   noCalls++;


void foo()

   DEFINE_COUNTER

【讨论】:

如果他这样做,他的打印功能将如何获取统计信息? @abarnert 几个选项——全局的map 即被宏修改,调用函数以数字为参数... 如果宏修改了全局映射,那么函数局部静态是干什么用的? @abarnert 这是一种不同的方法。没有足够的理由投反对票,但无论如何...... 您给出的答案没有回答用户的问题。然后你说你可以添加一个确实有效的解决方案,这是真的,但在这种情况下,你的回答并没有增加任何东西,只是首先使用其他解决方案。【参考方案5】:

使用实现观察者模式或方法调用拦截的库。您可以从this list 中选择一个,或使用Vitamin 之类的名称。

【讨论】:

以上是关于如何跟踪通话统计信息? C++的主要内容,如果未能解决你的问题,请参考以下文章

如何理解 Oracle 中跟踪文件的统计信息。如CPU、经过时间、查询...等

WebRTC Native M96数据统计-- 使用PeerConnection::GetStats获取WebRTC实时统计信息

如何从手动创建的 Firebase 动态链接中收集统计信息?

如何在python中使用openCV的连接组件和统计信息?

如何设置视频通话的持续时间

sql server发生的等待统计信息