簡體   English   中英

c ++線程開銷

[英]c++ thread overhead

我正在使用C ++中的線程,特別是使用它們來並行化地圖操作。

這是代碼:

#include <thread>
#include <iostream>
#include <cstdlib>
#include <vector>
#include <math.h>
#include <stdio.h>

double multByTwo(double x){
  return x*2;
}

double doJunk(double x){
  return cos(pow(sin(x*2),3));
}

template <typename T>
void map(T* data, int n, T (*ptr)(T)){
  for (int i=0; i<n; i++)
    data[i] = (*ptr)(data[i]);
}

template <typename T>
void parallelMap(T* data, int n, T (*ptr)(T)){
  int NUMCORES = 3;
  std::vector<std::thread> threads;
  for (int i=0; i<NUMCORES; i++)
    threads.push_back(std::thread(&map<T>, data + i*n/NUMCORES, n/NUMCORES, ptr));
  for (std::thread& t : threads)
    t.join();
}

int main()
{
  int n = 1000000000;
  double* nums = new double[n];
  for (int i=0; i<n; i++)
    nums[i] = i;

  std::cout<<"go"<<std::endl;

  clock_t c1 = clock();

  struct timespec start, finish;
  double elapsed;

  clock_gettime(CLOCK_MONOTONIC, &start);

  // also try with &doJunk
  //parallelMap(nums, n, &multByTwo);
  map(nums, n, &doJunk);

  std::cout << nums[342] << std::endl;

  clock_gettime(CLOCK_MONOTONIC, &finish);

  printf("CPU elapsed time is %f seconds\n", double(clock()-c1)/CLOCKS_PER_SEC);

  elapsed = (finish.tv_sec - start.tv_sec);
  elapsed += (finish.tv_nsec - start.tv_nsec) / 1000000000.0;

  printf("Actual elapsed time is %f seconds\n", elapsed);
}

使用multByTwo ,並行版本實際上稍慢(1.01秒而不是.95實時),而使用doJunk則更快(51與136實時)。 這對我意味着

  1. 並行化正在發揮作用
  2. 聲明新線程的開銷非常大。 有關為什么開銷如此之大,以及如何避免它的任何想法?

只是一個猜測:您可能會看到的是multByTwo代碼如此之快以至於您實現了內存飽和。 無論你向它投入多少處理器能力,代碼都不會運行得更快,因為它的速度已經達到了可以從RAM獲取的速度。

您沒有指定測試程序的硬件,也沒有指定編譯器版本和操作系統。 我確實在64位Scientific Linux下的四插槽Intel Xeon系統上嘗試了您的代碼,並從源代碼編譯了g++ 4.7。

首先在較舊的Xeon X7350系統上,我得到以下時間:

multByTwomap

CPU elapsed time is 6.690000 seconds
Actual elapsed time is 6.691940 seconds

multByTwo在3核上有parallelMap

CPU elapsed time is 7.330000 seconds
Actual elapsed time is 2.480294 seconds

並行加速是2.7倍。

doJunkmap

CPU elapsed time is 209.250000 seconds
Actual elapsed time is 209.289025 seconds

doJunk with parallelMap在3個核心上

CPU elapsed time is 220.770000 seconds
Actual elapsed time is 73.900960 seconds

並行加速是2.83x。

請注意,X7350來自相當古老的前Nehalem“Tigerton”系列,帶有FSB總線和位於北橋的共享內存控制器。 這是一個沒有NUMA效果的純SMP系統。

然后我在四插槽Intel X7550上運行你的代碼。 這些是Nehalem(“Beckton”)Xeons,內存控制器集成在CPU中,因此是一個4節點NUMA系統。 在一個套接字上運行並訪問位於另一個套接字上的內存的線程運行速度稍慢。 對於可能通過某些愚蠢的調度程序決策遷移到另一個套接字的串行進程也是如此。 從時間上看,綁定在這樣的系統中是非常重要的:

multByTwomap

CPU elapsed time is 4.270000 seconds
Actual elapsed time is 4.264875 seconds

multByTwomap綁定到NUMA節點0

CPU elapsed time is 4.160000 seconds
Actual elapsed time is 4.160180 seconds

multByTwomap綁定到NUMA節點0和CPU插槽1

CPU elapsed time is 5.910000 seconds
Actual elapsed time is 5.912319 seconds

mutlByTwo在3個核心上使用parallelMap

CPU elapsed time is 7.530000 seconds
Actual elapsed time is 3.696616 seconds

並行加速僅為1.13x(相對於最快的節點綁定串行執行)。 現在綁定:

multByTwo與3個核心上的parallelMap綁定到NUMA節點0

CPU elapsed time is 4.630000 seconds
Actual elapsed time is 1.548102 seconds

並行加速是2.69倍 - 與Tigerton CPU一樣多。

multByTwo與3個核心上的parallelMap綁定到NUMA節點0和CPU插槽1

CPU elapsed time is 5.190000 seconds
Actual elapsed time is 1.760623 seconds

並行加速比前一種情況的2.36倍 - 88%。

(我太doJunk等待doJunk代碼在相對較慢的Nehalems上完成,但我希望在Tigerton案例中有更好的表現)

但是有一個關於NUMA綁定的警告。 如果強制例如使用numactl --cpubind=0 --membind=0 ./program綁定到NUMA節點0,這將限制僅限於此節點的內存分配,並且在您的特定系統上,連接到CPU 0的內存可能不夠,並且很可能會發生運行時故障。

正如您所看到的,除了創建線程的開銷之外,還有一些因素可能會顯着影響您的代碼執行時間。 同樣在非常快的系統上,與每個線程完成的計算工作相比,開銷可能太高。 這就是為什么在提出有關並行性能的問題時,應該始終包含盡可能詳細的硬件和用於衡量性能的環境的細節。

多線程只能在更少的時間內在多核機器上完成更多的工作。

其他明智的他們只是輪流輪流時尚。

根據平台的不同,產生新線程可能是一項昂貴的操作。 避免這種開銷的最簡單方法是在程序啟動時生成一些線程並具有某種作業隊列。 我相信std :: async會為你做這件事。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM