使用TBB作为一个简单的例子

时间:2018-02-21 20:58:51

标签: c++ tbb

我是TBB的新手并尝试做一个简单的表达。

我的功能数据是:

int n = 9000000;
int *data = new int[n];

我创建了一个函数,第一个没有使用TBB

void _array(int* &data, int n) {
        for (int i = 0; i < n; i++) {
            data[i] = busyfunc(data[i])*123;
        }
}

It takes 0.456635 seconds.

还创建了一个to功能,第一个使用TBB

void parallel_change_array(int* &data,int list_count) {
    //Instructional example - parallel version
    parallel_for(blocked_range<int>(0, list_count),
        [=](const blocked_range<int>& r) {
        for (int i = r.begin(); i < r.end(); i++) {
            data[i] = busyfunc(data[i])*123;
        }
    });
}

我需要0.584889 seconds.

至于busyfunc(int m):

int busyfunc(int m)
{
    m *= 32;
    return m;
}

您能否告诉我,为什么不使用TBB的功能花费的时间少于TBB的功能?

我认为,问题在于功能很简单,而且在不使用TBB的情况下很容易计算。

2 个答案:

答案 0 :(得分:3)

首先,busyfunc()似乎不那么繁忙,因为9M元素只在半秒内计算出来,这使得这个例子相当于内存限制(未缓存的内存操作比算术运算多了几个数量级)。内存绑定计算的扩展程度不如计算限制,例如:普通内存复制通常可以扩展到不超过4倍,甚至可以在更大数量的内核/处理器上运行。

此外,内存绑定程序对NUMA效果更敏感,并且由于您使用标准C ++将此数组分配为连续内存,因此默认情况下它将完全分配在初始化发生的同一内存节点上。可以通过numactl -i all --运行来更改此默认值。

最后一个,但最重要的是TBB懒得且很慢地初始化线程。我想你不打算编写一个在并行计算花费0.5秒后退出的应用程序。因此,公平的基准应该考虑到实际应用中预期的所有预热效果。至少,它必须等到所有线程都启动并运行才能开始测量。 This answer提出了一种方法。

[update]请参阅Alexey的答案,了解潜在的编​​译器优化差异的另一个可能原因。

答案 1 :(得分:2)

除了Anton的asnwer之外,我还建议检查编译器是否能够等效地优化代码。

首先,检查由单个线程执行的TBB版本的性能,没有真正的并行性。您可以使用tbb::global_controltbb::task_scheduler_init将线程数限制为1,例如

tbb::global_control ctl(tbb::global_control::max_allowed_parallelism, 1);

当一个线程执行所有代码时,线程创建的开销以及缓存局部性或NUM​​A效应不应起作用。因此,您应该看到与无TBB版本大致相同的性能。如果你这样做,那么你就有可扩展性问题,Anton解释了可能的原因。

但是,如果您发现性能下降很多,那么这是一个串行优化问题。一个已知的原因是某些编译器不能优化blocked_range上的循环,因为它们优化了原始循环;并且还观察到将r.end()存储到局部变量可能会有所帮助:

    int rend = r.end();
    for (int i = r.begin(); i < rend; i++) {
        data[i] = busyfunc(data[i])*123;
    }