为什么使用指针查杀性能

时间:2014-12-03 16:47:26

标签: c++ performance inheritance allocation

我们说我有一个类Manager做了一些工作,然后我有一个DistributedManager,它继承自Manager但重新实现了一些使用多线程的方法。

使用它们的代码是

 Manager<T,W,P> manager(initargs);
 manager.compute(runargs);

 DistributedManager<T,W,P> manager(initargs, 4); // 4 is number of thread to use
 manager.compute(runargs);

然后在某些时候我想要通过命令修改我可以使用的线程数量。所以我创建了一个size_t nbthread = 1,它可以通过一个选项修改,我修改我的代码如下:

Manager<T,W,P>* manager;
switch(nbthread)
{
    case 0:
    case 1:
        manager = new Manager<T,W,P>(initargs);
        break;
    default:
        manager = new DistributedManager<T,W,P>(initargs, nbthread);
        break;
}
manager->compute(runargs);

它编译和工作......但我得到了crapy表演!

使用带有DistributedManager和4个线程的第一个方法,我可以在500毫秒运行,使用第二个方法,相同的计算运行超过2000毫秒。

分配部分不应该那么长:

sizeof(Manager<T,W,P>) : 104
sizeof(DistributedManager<T,W,P>) : 128

出了什么问题?

修改

使用

完成基准测试
std::chrono::high_resolution_clock::time_point t1 = std::chrono::high_resolution_clock::now();
densityfieldptr->compute(particles, massfield, massthreshold, densityfunctor);
std::chrono::high_resolution_clock::time_point t2 = std::chrono::high_resolution_clock::now();
printf("computation time: %ld ms\n", std::chrono::duration_cast<std::chrono::milliseconds>(t2 - t1).count());

编辑2:我是愚蠢的

时间的增加大约是因子4 ...因为非多线程版本是被调用的版本。使用指向基类的指针会使用方法!

virtual关键字不能用于解决这个问题,因为方法是模板化的

我不得不重新考虑我的模板模式,一切都按预期工作

2 个答案:

答案 0 :(得分:1)

我同意评论说没有足够的信息可以明确地说出正在发生的事情,但我有一个理论(false sharing)这个评论太长了,所以这是一个试探性的答案。

两者之间的主要区别是存储分配

  • DistributedManager管理器(initargs,4); //自动存储分配
  • manager = new DistributedManager(initargs,nbthread); //动态存储分配

自动存储在堆栈上实现,堆上的动态存储。你这么说什么? 那么“new”的实现并不能保证在缓存行边界和多个缓存行的分配。我之前遇到过性能问题,其中堆上的对象与另一个被另一个线程访问的对象共享,并且至少有一个对象被修改(由线程写入) - false sharing。这导致高速缓存行在线程运行的核心之间来回“乒乓”。它不是一个逻辑错误,但它是一个性能错误。编译器可以以防止错误共享的方式在堆栈上进行分配,或者它可能刚刚发生而不是那里的问题,但可能无法保证,更改某些内容在您的程序中,您可能也会看到自动存储的问题。解决这个问题的唯一方法是使用自定义分配器。

答案 1 :(得分:0)

使用指针可能会导致缓存未命中和分支未命中预测。但是如果没有分析器,你只能得到猜测,而不是一个可靠的答案。