在线程构建块中进行调试 [英] Debugging in threading building Blocks

查看:80
本文介绍了在线程构建块中进行调试的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想对带有任务的构建基块进行编程.但是实际上如何进行调试呢?

I would like to program in threading building blocks with tasks. But how does one do the debugging in practice?

通常,打印方法是调试程序的可靠技术. 根据我在MPI并行化方面的经验,正确的日志记录方法是每个线程在其自己的文件中打印调试信息(例如,在MPI_COMM_WORLD中排名为"debug_irank",其等级为irank),以便可以发现逻辑错误.

In general the print method is a solid technique for debugging programs. In my experience with MPI parallelization, the right way to do logging is that each thread print its debugging information in its own file (say "debug_irank" with irank the rank in the MPI_COMM_WORLD) so that the logical errors can be found.

TBB如何实现类似的目的?目前尚不清楚如何访问线程池中的线程号,因为这显然是tbb的内部功能.

How can something similar be achieved with TBB? It is not clear how to access the thread number in the thread pool as this is obviously something internal to tbb.

或者,可以添加一个额外的索引来指定生成任务时的等级,但这会使代码变得相当复杂,因为整个程序都必须照顾这一点.

Alternatively, one could add an additional index specifying the rank when a task is generated but this makes the code rather complicated since the whole program has to take care of that.

推荐答案

首先,使程序使用1个线程.为此,首先将task_scheduler_init构造为main,如下所示:

First, get the program working with 1 thread. To do this, construct a task_scheduler_init as the first thing in main, like this:

#include "tbb/tbb.h"

int main() {
    tbb::task_scheduler_init init(1);
    ...
}

请确保将宏TBB_USE_DEBUG设置为1进行编译,以便启用TBB的检查.

Be sure to compile with the macro TBB_USE_DEBUG set to 1 so that TBB's checking will be enabled.

如果单线程版本有效,但多线程版本无效,请考虑使用Intel Inspector找出竞争状况.确保使用TBB_USE_THREADING_TOOLS进行编译,以便Inspector获得足够的信息.

If the single-threaded version works, but the multi-threaded version does not, consider using Intel Inspector to spot race conditions. Be sure to compile with TBB_USE_THREADING_TOOLS so that Inspector gets enough information.

否则,我通常首先开始添加断言,因为机器可以比读取日志消息更快地检查断言.如果我真的对为什么断言失败感到困惑,我使用printfs和任务ID(而不是线程ID).创建任务ID的最简单方法是通过后递增tbb::atomic<size_t>并将其存储在任务中来分配一个ID.

Otherwise, I usually first start by adding assertions, because the machine can check assertions much faster than I can read log messages. If I am really puzzled about why an assertion is failing, I use printfs and task ids (not thread ids). Easiest way to create a task id is to allocate one by post-incrementing a tbb::atomic<size_t> and storing the result in the task.

如果我今天真的很糟糕,并且printfs正在更改程序行为,以便不会出现错误,请使用延迟的printfs".将printf参数填充到循环缓冲区中,并在检测到故障之后稍后在记录上运行printf.通常对于缓冲区,我使用包含格式字符串和一些word-size值的结构数组,并使数组的大小为2的幂.然后,原子增量和掩码足以分配插槽.例如,类似这样的内容:

If I'm having a really bad day and the printfs are changing program behavior so that the error does not show up, I use "delayed printfs". Stuff the printf arguments in a circular buffer, and run printf on the records later after the failure is detected. Typically for the buffer, I use an array of structs containing the format string and a few word-size values, and make the array size a power of two. Then an atomic increment and mask suffices to allocate slots. E.g., something like this:

const size_t bufSize = 1024;

struct record {
    const char* format;
    void *arg0, *arg1;
};

tbb::atomic<size_t> head;

record buf[bufSize];

void recf(const char* fmt, void* a, void* b) {
    record* r = &buf[head++ & bufSize-1];
    r->format = fmt;
    r->arg0 = a;
    r->arg1 = b;
}

void recf(const char* fmt, int a, int b) {
    record* r = &buf[head++ & bufSize-1];
    r->format = fmt;
    r->arg0 = (void*)a;
    r->arg1 = (void*)b;
}

两个recf例程记录格式和值.强制转换有点滥用,但是在大多数体系结构上,即使recf的第二次重载创建了记录,实际上也可以使用printf(r->format, r->arg0, r->arg1)正确打印记录.
〜 〜

The two recf routines record the format and the values. The casting is somewhat abusive, but on most architectures you can print the record correctly in practice with printf(r->format, r->arg0, r->arg1) even if the the 2nd overload of recf created the record.
~ ~

这篇关于在线程构建块中进行调试的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆