多线程:为什么两个程序比一个好?

时间:2013-11-13 09:35:03

标签: c++ multithreading pthreads numa

关于我的问题:

我的电脑配有2个AMD Opteron 6272插槽和64GB RAM。

我在所有32个核心上运行一个多线程程序,与运行2个程序的情况相比,速度降低了15%,每个程序都在一个16核心插槽上。

如何以两个程序的速度制作单程序版本?


更多详情:

我有大量任务,并希望完全加载系统的所有32个核心。 因此,我将任务分组打包1000个。这样的组需要大约120Mb的输入数据,并且需要大约10秒才能在一个核心上完成。为了使测试更加理想,我将这些组复制32次,并使用ITBB的parallel_for循环在32个核心之间分配任务。

我使用pthread_setaffinity_np来确保系统不会让我的线程在核心之间跳转。并确保所有核心都得到了相应的使用。

我使用mlockall(MCL_FUTURE)来确保系统不会让我的内存在套接字之间跳转。

所以代码看起来像这样:

  void operator()(const blocked_range<size_t> &range) const
  {
    for(unsigned int i = range.begin(); i != range.end(); ++i){

      pthread_t I = pthread_self();
      int s;
      cpu_set_t cpuset;
      pthread_t thread = I;
      CPU_ZERO(&cpuset);
      CPU_SET(threadNumberToCpuMap[i], &cpuset);
      s = pthread_setaffinity_np(thread, sizeof(cpu_set_t), &cpuset);

      mlockall(MCL_FUTURE); // lock virtual memory to stay at physical address where it was allocated

      TaskManager manager;
      for (int j = 0; j < fNTasksPerThr; j++){
        manager.SetData( &(InpData->fInput[j]) );
        manager.Run();
      }
    }
  }

只有计算时间对我很重要,因此我在单独的parallel_for循环中准备输入数据。并且不包括准备时间测量。

  void operator()(const blocked_range<size_t> &range) const
  {
    for(unsigned int i = range.begin(); i != range.end(); ++i){

      pthread_t I = pthread_self();
      int s;
      cpu_set_t cpuset;
      pthread_t thread = I;
      CPU_ZERO(&cpuset);
      CPU_SET(threadNumberToCpuMap[i], &cpuset);
      s = pthread_setaffinity_np(thread, sizeof(cpu_set_t), &cpuset);

      mlockall(MCL_FUTURE); // lock virtual memory to stay at physical address where it was allocated
      InpData[i].fInput = new ProgramInputData[fNTasksPerThr];

      for(int j=0; j<fNTasksPerThr; j++){
        InpData[i].fInput[j] = InpDataPerThread.fInput[j];
      }
    }
  }

现在我在32个核心上运行所有这些,并且每秒看到约1600个任务的速度。

然后我创建了两个版本的程序,tasksetpthread确保首先在第一个套接字的16个核心上运行,第二个 - 在第二个套接字上运行。我在shell中使用简单的&命令将它们一个接一个地运行:

program1 & program2 &

每个程序都达到~900个任务/秒的速度。总计这是> 1800个任务/秒,比一个程序版本多15%。

我想念什么?

我认为这可能是库中的问题,我只加载到集合线程的内存中。这可能是个问题吗?我可以复制库数据,以便它可以在两个套接字上独立使用吗?

2 个答案:

答案 0 :(得分:3)

我猜这是STL / boost内存分配,它会在numa节点上为你的集合传播内存,因为它们不是numa意识到你在每个节点上运行程序中的线程。

您使用的所有STL / boost事件的自定义分配器可能有所帮助(但可能是一项巨大的工作)。

答案 1 :(得分:1)

您可能遇到错误共享缓存的错误情况:http://en.wikipedia.org/wiki/False_sharing

您的线程可能通过block_range引用共享对相同数据结构的访问权限。如果速度就是您所需要的,您可能希望将副本传递给每个线程。如果您的数据太大而无法放入调用堆栈,您可以动态地在不同的缓存段中分配每个范围的副本(即确保它们足够远)。

或者我可能需要查看其余代码以了解您的工作做得更好。