10-кратное количество ошибок страниц при использовании одного ядра против двух и более ядер - PullRequest
0 голосов
/ 07 мая 2018

Я наблюдаю странное поведение в параллельном коде, который я разрабатываю с Python 3.6 / Numpy под Linux: запуск его на 2 ядрах примерно в 3 раза быстрее, чем на одном ядре. Я не уверен, что понимаю точную проблему, но мне удалось найти некоторые подсказки:

  1. Используя perf, я обнаружил, что с одним ядром получаю гораздо больше сбоев страниц, а не 2 (частичные прогоны - это долго выполняющиеся задания):

1 ядро ​​

   57414.449252      task-clock:u (msec)       #    1.000 CPUs utilized          
              0      context-switches:u        #    0.000 K/sec                  
              0      cpu-migrations:u          #    0.000 K/sec                  
     22,583,368      page-faults:u             #    0.393 M/sec                  
115,569,738,160      cycles:u                  #    2.013 GHz                    
 22,655,695,268      stalled-cycles-frontend:u #   19.60% frontend cycles idle   
 32,251,136,400      stalled-cycles-backend:u  #   27.91% backend cycles idle    
189,041,318,733      instructions:u            #    1.64  insn per cycle         
                                               #    0.17  stalled cycles per insn
 38,298,469,811      branches:u                #  667.053 M/sec                  
    654,389,291      branch-misses:u           #    1.71% of all branches        
 83,343,166,583      L1-dcache-loads:u         # 1451.606 M/sec                  
    318,284,548      L1-dcache-load-misses:u   #    0.38% of all L1-dcache hits  
 25,589,478,853      L1-icache-loads:u         #  445.698 M/sec                  
  2,560,674,130      L1-icache-load-misses:u   #   10.01% of all L1-icache hits  
 83,377,552,384      dTLB-loads:u              # 1452.205 M/sec                  
    216,145,062      dTLB-load-misses:u        #    0.26% of all dTLB cache hits 
 25,626,979,550      iTLB-loads:u              #  446.351 M/sec                  
     55,847,490      iTLB-load-misses:u        #    0.22% of all iTLB cache hits 
        222,176      L1-dcache-prefetches:u    #    0.004 M/sec                  
        220,870      L1-dcache-prefetch-misses:u #    0.004 M/sec                

2 ядра

  111569.269846      task-clock:u (msec)       #    1.837 CPUs utilized         
              0      context-switches:u        #    0.000 K/sec                 
              0      cpu-migrations:u          #    0.000 K/sec                 
      4,415,116      page-faults:u             #    0.040 M/sec                 
330,750,606,867      cycles:u                  #    2.965 GHz                   
 69,517,418,862      stalled-cycles-frontend:u #   21.02% frontend cycles idle  
104,365,888,484      stalled-cycles-backend:u  #   31.55% backend cycles idle   
609,338,385,634      instructions:u            #    1.84  insn per cycle        
                                               #    0.17  stalled cycles per ins
125,341,622,204      branches:u                # 1123.442 M/sec                 
  1,860,702,685      branch-misses:u           #    1.48% of all branches       
261,941,396,767      L1-dcache-loads:u         # 2347.792 M/sec                 
    935,372,306      L1-dcache-load-misses:u   #    0.36% of all L1-dcache hits 
 79,306,212,694      L1-icache-loads:u         #  710.825 M/sec                 
  7,337,551,130      L1-icache-load-misses:u   #    9.25% of all L1-icache hits 
261,578,594,355      dTLB-loads:u              # 2344.540 M/sec                 
    425,510,820      dTLB-load-misses:u        #    0.16% of all dTLB cache hits
 79,008,997,415      iTLB-loads:u              #  708.161 M/sec                 
    102,332,385      iTLB-load-misses:u        #    0.13% of all iTLB cache hits
        944,223      L1-dcache-prefetches:u    #    0.008 M/sec                 
        945,497      L1-dcache-prefetch-misses:u #    0.008 M/sec               
  1. Запуск perf top указывает на то, что ~ 35% времени тратится на ядро, и эти функции являются основными участниками:

    clear_page_rep
    page_fault
    check_preemption_disabled
    get_page_from_freelist
    __handle_mm_fault
    free_pcppages_bulk
    __pagevec_lru_add_fn
    sync_regs release_pages
    handle_mm_fault
    preempt_count_add
    .
  2. Мне кажется, что по крайней мере часть проблемы возникает при копировании массива Numpy размером ~ 15 × 3500, но не каждый раз. Код по существу вычисляет модели на декартовой сетке физических параметров и делает частые копии объекта, который содержит несколько массивов Numpy. Теперь, для некоторых копий (но не всех) этого объекта копия этого массива ~ 15 × 3500 Numpy занимает ~ 15 раз больше, я полагаю из-за ошибки страницы. Код часто создает / удаляет эти объекты, но общая память, связанная с каждым процессом, изменяется медленно (результаты записываются в общий RawArray), поэтому я считаю, что память обычно должна быть перераспределена (создание новых объектов происходит вскоре после уничтожения предыдущего). Это происходит только тогда, когда используется одно ядро, а не при использовании большего количества ядер.

  3. Я попытался запустить два независимых запуска, используя по 1 ядру одновременно, и у них обоих большое количество ошибок страниц. Так что это не похоже на одновременное выполнение нескольких процессов.

  4. При работе на одном ядре я пробовал как с многопроцессорной обработкой (spawn и fork), так и без, результат один и тот же, с большим количеством сбоев страниц. Так что это, похоже, не связано

  5. Это происходит как с пакетами Python Archlinux, так и с пакетами Anaconda.

  6. Это происходит как на AMD EPYC 7351 с 512 ГБ или ОЗУ, так и на Intel Core i7 6700K с 64 ГБ ОЗУ (оба работают под управлением Archlinux). Код использует ~ 10 ГБ ОЗУ, в основном в виде RawArrays для хранения результата. Объем оперативной памяти, используемой в отдельных объектах, должен составлять ~ 500 кБ на объект, и в любой момент времени в оперативной памяти их должно быть не более 7-8.

Я немного растерялся из-за того, почему я получаю так много сбоев страниц при работе на одном ядре, а не при работе на ядрах 2+. Есть идеи, что может происходить и возможные способы это исправить? Я пытаюсь измерить, как масштабируется код с количеством ядер, поэтому мне нужна надежная точка данных при работе на одном ядре. As использует 2 ядра в 3 раза быстрее, чем работает на одном ядре.

...