首页 > 解决方案 > 重塑微软的 Concurrency::diagnostic::span 也可以检测外跨度

问题描述

我希望创建ScopePrinterMicrosoft 类似的 Concurrency::diagnostic::span工作,
但它也可以检测封装范围。

ScopePrint sc2{"scope1"}; // should print "[start scope1]"
createTask([&](){
    ScopePrint sc2{"scope2"};   // should print "[start scope1/scope2]"
    //do something expensive (can create more tasks & ScopePrints)
    // should print "[end scope1/scope2]"
});
// should print "[end scope1]"

这是我的MCVE

fib()并且fibPrint()只是一个昂贵的虚拟功能。

ScopePrinter(X)[begin X]是我在构造函数处打印并[end X]在其块结束时 打印的实用程序。

int fib(int n) { // just something that burn some CPU cycles
    if (n<2) return n;
    return fib(n-1) + fib(n-2);
}
void fibPrint(int n) {  // just something that burn some CPU cycles
    std::cout<<n<<" fib= "<<fib(n)<<std::endl;
}

struct ScopePrinter{ // my Utility class - useful for profiling
    std::string name="";
    public: ScopePrinter(std::string strP){
        name=strP;  std::cout<< ("[start "+name +"]\n");
        //in real case, it cache current time too
    }
    public: ~ScopePrinter(){
       std::cout<< ("[end "+name +"]\n"); 
       //in real case, it prints total used time too
    }
};

这是main():-

int main() {
    auto a1 = std::async([&](){
        ScopePrinter s("a1");
        fibPrint(5);
    } );
    auto a2 = std::async([&](){
        ScopePrinter s("a2");
        fibPrint(6);
    } );
    auto a3 = std::async([&](){
        ScopePrinter s("a3");
        fibPrint(7);
        {
            auto a31 = std::async([&](){
                ScopePrinter s("a31");
                fibPrint(8);
            } );
            auto a32 = std::async([&](){
                ScopePrinter s("a32");
                fibPrint(9);
            } );
        }
    } );
    a1.wait();
}

这是一个可能的输出:-

[start a1]
[start a2]
5 fib= 6 fib= 58
[end a1]

[end a2]
[start a3]
7 fib= 13
[start a31]
8 fib= 21
[end a31]
[start a32]
9 fib= 34
[end a32]
[end a3]

如何使ScopePrinter("a31")构造函数和析构函数打印完整范围,例如[start a3/a31]and[end a3/a31]而不是[start a31]and [end a31]

这对于分析我的多线程程序非常有用。
我正在考虑thread_local和 MACRO,但我认为这不会有帮助。

我已阅读是否多次使用 std::async 对小任务性能友好?.

标签: c++multithreadingprofilingc++17raii

解决方案


如果您希望a3/a31a3/a32显示为子范围,那么您只需将指针传递给外部范围,并使用它来构建一个复合名称:

struct ScopePrinter {
  std::string name;

 public:
  ScopePrinter(std::string strP, ScopePrinter* parent = nullptr)
      : name((parent ? parent->name + "/" : "") + strP) {
    std::cout << ("[start " + name + "]\n");
  }

 public:
  ~ScopePrinter() { std::cout << ("[end " + name + "]\n"); }
};

然后在嵌套调用的情况下,您可以传入外部范围:

  auto a3 = std::async([&]() {
    ScopePrinter s("a3");
    fibPrint(7);
    {
      auto a31 = std::async([&]() {
        ScopePrinter s1("a31", &s);
        fibPrint(8);
      });
      auto a32 = std::async([&]() {
        ScopePrinter s2("a32", &s);
        fibPrint(9);
      });
    }
  });

这将打印如下内容:

[start a1]
5 fib= 5
[end a1]
[start a3]
7 fib= [start a2]
6 fib= 138
[end a2]

[start a3/a31]
8 fib= [start a3/a32]
9 fib= 34
[end a3/a32]
21
[end a3/a31]
[end a3]

推荐阅读