在线程构建块中进行调试

时间:2015-10-01 12:00:46

标签: multithreading debugging mpi tbb

我想在使用任务的线程构建块中编程。但是如何在实践中进行调试呢?

通常,print方法是一种用于调试程序的可靠技术。 根据我使用MPI并行化的经验,正确的日志记录方法是每个线程在其自己的文件中打印其调试信息(例如" debug_irank" irank在MPI_COMM_WORLD中排名),以便逻辑错误可以找到。

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

或者,可以在生成任务时添加一个指定排名的附加索引,但这使得代码相当复杂,因为整个程序必须处理它。

1 个答案:

答案 0 :(得分:3)

首先,让程序使用1个线程。为此,请在task_scheduler_init中构建main作为第一件事,如下所示:

#include "tbb/tbb.h"

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

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

如果单线程版本有效,但多线程版本没有,请考虑使用Intel Inspector来发现竞争条件。请务必使用TBB_USE_THREADING_TOOLS进行编译,以便Inspector获取足够的信息。

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

如果我有一个非常糟糕的一天并且printfs正在改变程序行为以便错误没有显示,我使用&#34;延迟printfs&#34;。将printf参数填入循环缓冲区,然后在检测到故障后在记录上运行printf。通常对于缓冲区,我使用包含格式字符串和几个字大小值的结构数组,并使数组大小为2的幂。然后原子增量和掩码足以分配插槽。例如,像这样:

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例程记录格式和值。该演员有点滥用,但在大多数架构中,即使printf(r->format, r->arg0, r->arg1)的第二次重载创建了该记录,您也可以使用recf在练习中正确打印记录。 〜 〜