非対話型、メモリ集約型プロセスの速度が実行中のプロセスの数(および修正方法)によって異なります。

非対話型、メモリ集約型プロセスの速度が実行中のプロセスの数(および修正方法)によって異なります。

基本的な質問のようですが、どこでも見つけることができませんね。マルチコアシステムで多くのメモリ集約プロセスを実行してより多くのスループットを得ることを望んでいます。これらのプロセス間に通信はありません。

私はできます。予想される各プロセスの完了時間は、プロセスの数が物理コアの数(私の場合は16)に近づくまで、実行中のプロセスの数とほぼ独立しています。

観察する完了時間ゆっくり曲線は、16個のプロセスが同時に実行されるまで拡張されます。各プロセスは、1つのプロセスしか実行されていない場合よりも約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)

おすすめ記事