为什么在多goroutine中读写切片会花费很多呢?

时间:2019-04-24 16:29:56

标签: go goroutine pprof

我已经编写了一个多goroutine版本的mergeSort,还编写了一个基准测试。现在,我想使用“去工具pprof”来分析代码的瓶颈。

当我获得cpu配置文件时,我在pprof中使用“ top10”来获得以下输出:

"folders": {  
  $userId: {
    'depth_0': {
      $auto_generated_folder_id_1: {
        'name': 'first_project',
        'parentId': '',
        'projectIds': [] // not all sub projectIds, only first level children
        ...
      },
      $auto_generated_folder_id_1: {
        'name': 'first_project',
        'parentId': '',
        'projectIds': [] // // not all sub projectIds, only first level children
        ...
      },
    },
    'depth_1': {
      $auto_generated_folder_id_1: {
        'name': 'first_project',
        'parentId': 'id_amongst_depth_0',
        'projectIds': [] // not all sub projectIds, only first level children
        ...
      },
      $auto_generated_folder_id_1: {
        'name': 'first_project',
        'parentId': 'id_amongst_depth_0',
        'projectIds': [] // not all sub projectIds, only first level children
        ...
      },
    },
    'depth_2': {
      $auto_generated_folder_id_1: {
        'name': 'first_project',
        'parentId': 'id_amongst_depth_1',
        'projectIds': [] // not all sub projectIds, only first level children
        ...
      },
      $auto_generated_folder_id_1: {
        'name': 'first_project',
        'parentId': 'id_amongst_depth_0',
        'projectIds': [] // not all sub projectIds, only first level children
        ...
      },
    },
    ...    
  }
}

"projects": {  
  $userId: {    
    $auto_generated_project_id_1: {
      'name': 'first_project',
      'folderId': 'id_of_folder'
      ...
    },
    $auto_generated_project_id_2: {
      'name': 'first_project',
      'folderId': 'id_of_folder'
      ...
    },
  }
}

从上面,我认为好奇的瓶颈在sort.Merge中,因此我使用“列表合并”来深入研究该方法,并找到以下信息:

Showing nodes accounting for 4.73s, 98.54% of 4.80s total
Dropped 21 nodes (cum <= 0.02s)
Showing top 10 nodes out of 30
      flat  flat%   sum%        cum   cum%
     3.66s 76.25% 76.25%      3.66s 76.25%  pingcap/talentplan/tidb/common/alg/sort.Merge
     0.62s 12.92% 89.17%      0.64s 13.33%  pingcap/talentplan/tidb/mergesort.prepare
     0.17s  3.54% 92.71%      0.17s  3.54%  runtime.freedefer
     0.12s  2.50% 95.21%      0.14s  2.92%  pingcap/talentplan/tidb/common/alg/sort.quickSort
     0.10s  2.08% 97.29%      0.10s  2.08%  runtime.memmove
     0.03s  0.62% 97.92%      0.03s  0.62%  runtime.memclrNoHeapPointers
     0.03s  0.62% 98.54%      0.04s  0.83%  runtime.stackpoolalloc
         0     0% 98.54%      0.11s  2.29%  pingcap/talentplan/tidb/common/alg/sort.MergeSortByMultiGoroutine
         0     0% 98.54%      0.14s  2.92%  pingcap/talentplan/tidb/common/alg/sort.QuickSort
         0     0% 98.54%      4.04s 84.17%  pingcap/talentplan/tidb/common/alg/sort.mergeSortByMultiGoroutine

让我困惑的是这里!在合并方法中,有4个for循环。第一个for循环和第4个for循环的比例大致相同,它们的任务都是将元素从一个切片移动到另一个切片。问题是,为什么第一个for-loop成本如此之高(1.58s加1.52s),而第四个for-loop成本却如此之少(仅为170ms)?这是违反直觉的!

该项目的github地址为https://github.com/Duncan15/talent-plan/tree/master/tidb/mergesort。您可以使用“ make pprof”运行基准测试并获取cpu配置文件和内存配置文件。

我想知道为什么会这样,如果有时间,请阅读我的代码并给我一些建议。

谢谢你告诉我!!

我编写了一些代码来验证当Merge方法在单goroutine环境中运行时,第一个for循环的成本与第四个for循环的成本大致相同,这似乎很直观。因此,我认为多goroutine环境是否会导致上述现象。但是在多goroutine环境中,Merge方法可以同时运行。换句话说,第1个for循环和第4个for循环同时运行,如果同时进行读写分片会增加成本,那么第4个for循环的成本也必须增加,但是从pprof输出中我们可以发现只有第一个for-loop的成本增加!

我还写了另一个测试来验证我的想法。您可以使用“ make vet”来运行它。此测试同时运行Merge方法。与多goroutine版本的mergeSort的不同之处在于,此测试没有有关排序的代码,而只是有关合并的代码。而且我惊讶地发现,在此测试中,第一个for循环的成本与第四个for循环的成本大致相同。所以最后我完全困惑了!

0 个答案:

没有答案