内存分配太慢

Memory allocation is too slow

我是 运行 AWS 上的一些内存密集型服务(m4.15xlargem5.12xlarge 实例)并且注意到在特定条件下(通常在特定内存限制之后)时间分配额外内存的时间急剧增加(高达 50 倍)。

发生时 htop 在内核模式下显示 100%(红色)并且 perf top 看起来像这样:

  62.82%  [kernel]                                           [k] pageblock_pfn_to_page                                                              
  15.21%  [kernel]                                           [k] clear_page_c_e                                                                     
   5.78%  [kernel]                                           [k] get_pfnblock_flags_mask                                                            
   3.06%  [kernel]                                           [k] compaction_alloc                                                                   
   1.59%  [kernel]                                           [k] clear_huge_page                                                                    
   1.49%  [kernel]                                           [k] _raw_spin_lock                                                                     
   1.41%  [kernel]                                           [k] async_page_fault                                                                   
   1.29%  a.out                                              [.] memTest                                                                            
   0.99%  [kernel]                                           [k] get_page_from_freelist                                                             
   0.85%  [kernel]                                           [k] compact_zone                                                                       
   0.69%  [kernel]                                           [k] try_charge                                                                         
   0.51%  [kernel]                                           [k] error_entry                                                                        
...

在正常操作期间,它看起来像这样:

  66.29%  [kernel]                                           [k] clear_page_c_e
   7.05%  [kernel]                                           [k] clear_huge_page
   3.91%  a.out                                              [.] memTest
   3.66%  [kernel]                                           [k] _raw_spin_lock
   3.12%  [kernel]                                           [k] async_page_fault
   2.68%  [kernel]                                           [k] get_page_from_freelist
   1.93%  [kernel]                                           [k] _cond_resched
   1.49%  [kernel]                                           [k] try_charge
   1.12%  [kernel]                                           [k] error_entry
   1.01%  [kernel]                                           [k] retint_user
   0.93%  [kernel]                                           [k] handle_mm_fault
   0.77%  [kernel]                                           [k] mem_cgroup_try_charge
   0.67%  [kernel]                                           [k] pmd_pfn
   0.66%  [kernel]                                           [k] __rmqueue.isra.80
...

我不完全明白是什么触发了这种行为。有时很难重现,但有时它一直在发生。

我假设这与 AWS 虚拟化有关(所以问题是间歇性的,我将其归因于 "neighbors") (请参阅下面的 更新)。我也无法在 m5.metal 机器上重现该问题。

我能够使用一个在循环中分配和初始化内存的简单 C 程序重现该问题:

void memTest(long chunk, long total) {
    struct timeval prev, cur = {0,0}, lastProgress = {0,0};
    int i, j;
    int num = total / chunk;
    int p, progress = -1;
    uint8_t *data[num];

    get_now(&prev);

    for (i = 0; i < num; i++) {
        data[i] = malloc(chunk);
        for (j = 0; j < chunk; j += 4096) {
            data[i][j] = rand()%265;
        }

        get_now(&cur);
        add(delta(&prev, &cur));
        prev = cur;

        p = (i * 20) / num * 5;

        if (p != progress) {
            if (lastProgress.tv_sec == 0) {
                printf("%s: %02d%%\n", format(&cur), p);
            } else {
                double elapsed = delta(&lastProgress, &cur);
                printf("%s: %02d%% (%gms)\n", format(&cur), p, elapsed);
            }
            lastProgress = cur;
            progress = p;
        }
    }
}
m5.12xlarge$ ./a.out --total 182714368000 --chunk 16777216
2019-03-27 05:03:22.805827: 00%
2019-03-27 05:03:25.035575: 05% (2229.75ms)
2019-03-27 05:03:27.244955: 10% (2209.38ms)
2019-03-27 05:03:29.458160: 15% (2213.2ms)
2019-03-27 05:03:31.665313: 20% (2207.15ms)
2019-03-27 05:03:33.871949: 25% (2206.64ms)
2019-03-27 05:03:36.075955: 30% (2204.01ms)
2019-03-27 05:03:38.284512: 35% (2208.56ms)
2019-03-27 05:03:40.489039: 40% (2204.53ms)
2019-03-27 05:03:42.697444: 45% (2208.41ms)
2019-03-27 05:03:44.902268: 50% (2204.82ms)
2019-03-27 05:03:47.110703: 55% (2208.43ms)
2019-03-27 05:03:49.315001: 60% (2204.3ms)
2019-03-27 05:03:51.523370: 65% (2208.37ms)
2019-03-27 05:03:53.728535: 70% (2205.16ms)
2019-03-27 05:03:55.936081: 75% (2207.55ms)
2019-03-27 05:03:58.141149: 80% (2205.07ms)
2019-03-27 05:04:00.349740: 85% (2208.59ms)
2019-03-27 05:04:02.553894: 90% (2204.15ms)
2019-03-27 05:04:04.762675: 95% (2208.78ms)
2019-03-27 05:04:41.470692: 100% (36708ms) .  <---

这次我只能解决接近内存限制的问题,但即使在 20Gb(可用的 186Gb)上我也能解决。

如果有人能说明正在发生的事情以及如何消除这种影响,我将不胜感激。

更新:

我更深入地研究了这个问题,目前责怪恰好在 m5.12xlarge 上启用 (always) 和禁用 (madvise) 的透明大页面支持 (THP)在 m5.metal 上。切换机器上的设置后,我在 m5.metal 上遇到了问题,在 m5.12xlarge.

上问题消失了

正如更新部分所建议的那样,问题似乎是由透明大页面引起的(THP) support. It already has been found problematics by MongoDB and some others link, link,建议禁用。

关于如何在 Whosebug 和 stackexchange 上禁用 THP 有很多问题。例如: