비대화형, 메모리 집약적 프로세스의 속도가 실행 중인 프로세스 수(및 수정 방법)에 따라 달라지는 이유는 무엇입니까?

비대화형, 메모리 집약적 프로세스의 속도가 실행 중인 프로세스 수(및 수정 방법)에 따라 달라지는 이유는 무엇입니까?

기본적인 질문인 것 같은데 어디서도 찾을 수가 없네요. 멀티 코어 시스템에서 많은 메모리 집약적 프로세스를 실행하여 더 많은 처리량을 얻기를 바라고 있습니다. 이러한 프로세스 간에는 통신이 없습니다.

저 할 수 있어요예상되는각 프로세스의 완료 시간은 프로세스 수가 물리적 코어 수(제 경우에는 16개)에 가까워질 때까지 실행 중인 프로세스 수와 거의 독립적입니다.

관찰하다완료 시간서서히곡선은 16개의 프로세스가 동시에 실행될 때까지 확장됩니다. 각 프로세스는 하나의 프로세스만 실행될 때보다 약 3배 느리게 실행됩니다.

속도를 늦추는 것은 무엇입니까? ("컨텍스트 전환"이라는 단어보다 더 구체적으로 설명해주세요.) 이에 대해 제가 할 수 있는 일이 있나요?

편집하다:Michael Homer는 내가 CPU 집약적인 프로세스가 아닌 메모리 집약적인 프로세스에 관심이 있다고 지적했습니다. 이 모든 CPU가 메모리 버스를 공유하는데, 이것이 병목 현상을 일으킬 수 있다고 생각합니다. 이상적으로는 프로세스 메모리를 CPU에 "더 가깝게" 가져오는 일종의 NUMA 아키텍처를 원합니다. 이는 이 문제를 해결하려면 다른 하드웨어를 찾아야 한다는 뜻인가요?

세부사항은 다음과 같습니다:

quickie2.py무작위로 CPU를 많이 사용하는 작업을 수행하는 간단한 스크립트가 있습니다 . bash 명령줄을 사용하여 N개의 프로세스를 한 번에 시작합니다. 아래와 같이 14개의 프로세스가 있습니다.

for x in 1 2 3 4 5 6 7 8 9 10 11 12 13 14; do (python quickie2.py &); done

각 N의 완료 시간은 다음과 같습니다.

N_proc  Time to completion (sec)
1       7.29
2       7.28  7.30
3       7.27  7.28  7.38
4       7.01  7.19  7.34  7.43
5       8.41  8.94  9.51  10.27  11.73
6       7.49  7.79  7.97  10.01  10.58  10.85
7       7.71  8.72  10.22  10.43  10.81  10.81  11.42
8       10.1  10.16  10.27  10.29  10.48  10.60  10.66  10.73
9       9.94  11.20  11.27  11.35  11.61  12.43  12.46  12.99  13.53
10      9.26  12.54  12.66  12.84  12.95  13.03  13.06  13.52  13.93  13.95
11      12.46  12.48  12.65  12.74  13.69  13.92  14.14  14.39  14.40  14.69  17.13
12      13.48  13.49  13.51  13.58  13.65  13.67  14.72  14.87  14.89  14.94  15.01  15.06
13      15.47  15.51  16.72  16.79  16.79  16.91  17.00  17.45  17.75  17.78  17.86  18.14  18.48
14      15.14  15.22  16.47  16.53  16.84  17.78  18.07  19.00  19.12  19.32  19.63  19.71  19.80  19.94
15      18.05  18.18  18.58  18.69  19.84  20.70  21.82  21.93  22.13  22.44  22.63  22.81  22.92  23.23  23.23
16      20.96  21.00  21.10  21.21  22.68  22.70  22.76  22.82  24.65  24.66  25.32  25.59  26.16  26.22  26.31  26.38

편집하다:그건 그렇고, 프로세스를 코어에 고정하면 부패가 더 심해집니다. 아래 코드 목록에서 주석 처리된 줄을 참조하세요.

N_proc  Time to completion (sec) with CPU-pinning
1       6.95 
2       10.11  10.18 
4       19.11  19.11  19.12  19.12 
8       20.09  20.12  20.36  20.46  23.86  23.88  23.98  24.16 
16      20.24  22.10  22.22  22.24  26.54  26.61  26.64  26.73  26.75  26.78  26.78  26.79  29.41  29.73  29.78  29.90 

다음은 실제로 N(여기서는 14) 코어가 사용 중임을 보여주는 htop의 스크린샷입니다.

  1  [|||||||||||||||98.0%]    5  [||              5.8%]     9  [||||||||||||||100.0%]    13 [                0.0%]
  2  [||||||||||||||100.0%]    6  [||||||||||||||100.0%]     10 [||||||||||||||100.0%]    14 [||||||||||||||100.0%]
  3  [||||||||||||||100.0%]    7  [||||||||||||||100.0%]     11 [||||||||||||||100.0%]    15 [||||||||||||||100.0%]
  4  [||||||||||||||100.0%]    8  [||||||||||||||100.0%]     12 [||||||||||||||100.0%]    16 [||||||||||||||100.0%]
  Mem[|||||||||||||||||||||||||||||||||||||3952/64420MB]     Tasks: 96, 7 thr; 15 running
  Swp[                                        0/16383MB]     Load average: 5.34 3.66 2.29 
                                                             Uptime: 76 days, 06:59:39

완전성을 기하기 위해 여기에 몇 가지 작업을 수행하는 Python 프로그램이 있습니다. 중요한 것은 CPU를 바쁘게 유지한다는 것입니다.

# Code of quickie2.py (for completeness).

import numpy
import time

# import psutil
# psutil.Process().cpu_affinity([int(sys.argv[1])])

arena = numpy.empty(240*1024**2, dtype=numpy.uint8)

startTime = time.time()

# just do some work that takes a lot of CPU
for i in range(100):
    one = arena[:80*1024**2].view(numpy.float64)
    two = arena[80*1024**2:160*1024**2].view(numpy.float64)
    three = arena[160*1024**2:].view(numpy.float64)
    three = one + two

print(" {:.2f} ".format(time.time() - startTime))

답변1

이제 무엇이 잘못되었는지 이해하고 이것이 UNIX 제한이 아니라 하드웨어 제한이라는 것을 알고 있으므로 여기에 게시하는 것이 적합하지 않습니다. 그러나 마무리 발언을 추가해야겠다고 생각했습니다.

메모리가 제한된 독립 실행형 프로세스에 메모리 대역폭 문제가 있습니다. Knights Landing 프로세서에서 프로세스를 반복하고 로컬 MCDRAM에 Numpy 어레이를 할당하는 방법을 배웠습니다. 로컬 메모리를 사용하면 메모리 버스에 경합이 없으며 프로세스는 기본 하드웨어에서 관찰한 한도를 훨씬 초과하여 계속해서 확장됩니다.

이벤트 비율 및 독립 프로세스 수

MCDRAM(일반 RAM 대신)에 Numpy 배열을 할당하는 방법은 다음과 같습니다.

import ctypes
import numpy

def malloc_mcdram(size):
    libnuma = ctypes.cdll.LoadLibrary("libnuma.so")
    assert libnuma.numa_available() == 0   # NUMA not available is -1

    libnuma.numa_alloc_onnode.restype = ctypes.POINTER(ctypes.c_uint8)
    return libnuma.numa_alloc_onnode(ctypes.c_size_t(size), ctypes.c_int(1))

def custom_allocator_array(allocator, size, dtype):
    ptr = allocator(size)
    ptr.__array_interface__ = {"version": 3,
                               "typestr": numpy.ctypeslib._dtype(type(ptr.contents)).str,
                               "data": (ctypes.addressof(ptr.contents), False),
                               "shape": (size,)}
    return numpy.array(ptr, copy=False).view(dtype)

myarray = custom_allocator_array(malloc_mcdram, sizeInBytes, numpy.float64)

답변2

프로세스는 CPU가 아닌 메모리가 무겁습니다. 이 시도:

#!/usr/bin/env python

import datetime
import hashlib

data = "\0" * 64

ts_start = datetime.datetime.now()
for i in range(10000000):
    data = hashlib.sha512(data).digest()
ts_end = datetime.datetime.now()
print("Elapsed: %s" % (ts_end - ts_start))

2소켓/8코어/16스레드 시스템에서 최대 8개의 런타임을 병렬로 실행하면 완료하는 데 약 20초가 걸리는 일관된 결과를 얻습니다. 또한 프로세스가 CPU 리소스를 놓고 경쟁하기 시작하면 성능이 저하됩니다.

단일 실행:

~$ python cpuheavy.py 
Elapsed: 0:00:20.461652

8개 병렬(= 코어당 1개), 여전히 동일한 시간:

~$ for i in $(seq 8); do python cpuheavy.py & done
Elapsed: 0:00:18.979012
Elapsed: 0:00:19.092770
Elapsed: 0:00:19.873763
Elapsed: 0:00:20.139105
Elapsed: 0:00:20.147066
Elapsed: 0:00:20.181319
Elapsed: 0:00:21.328754
Elapsed: 0:00:21.495310

16번 병렬로 실행(= 하이퍼스레드당 1번), 프로세스가 CPU 시간 경쟁을 시작하면서 시간이 약 31초로 늘어났습니다. Ca 시간이 50% 증가했습니다.

32개의 프로세스를 병렬로 실행하려면 CPU 스레드를 공유해야 하므로 성능이 저하됩니다. 각 프로세스의 완료 시간은 2분 이상으로 늘어났습니다(시간 4배 증가).

관련 정보