プロセスメモリ全体をスワップから素早くロードする方法 (Linux)?

プロセスメモリ全体をスワップから素早くロードする方法 (Linux)?

私は CPU を大量に消費するプロセスを並列で実行しています。通常、これらのプロセスはそれぞれ数 GB のメモリを使用します。時々、大量のメモリも割り当てます (たとえば 150~250 GB)。通常は、最大で 1 つのプロセスがこれを実行するため、使用可能な RAM (私のマシンでは 384 GB) に収まります。ただし、複数のプロセスが同時にこの大量のメモリを割り当てる場合があり、(当然ですが) スワッピングのためにすべてが遅くなります。

このような場合、メモリを大量に消費するプロセスを 1 つだけ残して停止し、効率的に計算できるようにします。ただし、停止したプロセスをスワップインするには、ランダム アクセス パターンでディスクから数十ギガバイトをロードする必要があるため、時間がかかります。そこで疑問になるのが、単一のプロセスにスワップからコア全体を順番にロードさせるにはどうすればよいかということです。

これまでのところ、私は swappiness カーネル ヒントしか見つけていません。これは (cgroup の助けを借りて) プロセスがそれ以上スワップするのを防ぐことができますが、スワップ解除のパフォーマンスには役立ちません。停止した他のプロセスがスワップ領域を占有する必要があるため、すべてのスワップをオフにすることは明らかに不可能です。

独自のミニ スケジューラを構築することもできません。プロセスは Python のさまざまな小さなスクリプト/プログラムであり、メモリのピークは通常ライブラリ呼び出しで発生するため、ピークがいつ発生するかを予測することはできません。


明確にしておきますが、私はテラバイト単位の RAM を購入することは考えていません。この規模では高価すぎるからです。SSD/SSD アレイにスワップを配置しても、ほんの少ししか効果がありません (測定済み)。そのため、これも私が求めている解決策ではありません。


(部分的な自己回答):

カーネル ハッキングなしでは、本当にシーケンシャルなスワップ読み取り (単一プロセスに属するページのみ) はほとんど不可能のようです。測定してみたswapoff -aところ、スワップはシーケンシャルに読み取られませんでした。また、このような最適化を簡単に実装できる場合は、読み取り速度が速くなるのが理にかなっています。

/proc/[pid]/mem現在、私の最善のアプローチは、以下のスクリプト (root として実行する必要があります) を使用して、疑似ファイルを通じてプロセス メモリ全体を読み取ることです。

#!/usr/bin/python2
import re
import sys
pid=str(sys.argv[1]) # process pid given by the first arg

print(pid) # just to aviod mistakes

CHUNKSIZE=10485760  # single read() invocation block size

total=0
maps_file = open("/proc/"+pid+"/maps", 'r')
mem_file = open("/proc/"+pid+"/mem", 'r', 0)
for line in maps_file.readlines():  # for each mapped region
    m = re.match(r'([0-9A-Fa-f]+)-([0-9A-Fa-f]+) ([-r])', line)
    if m.group(3) == 'r':  # if this is a readable region
        start = int(m.group(1), 16)
        end = int(m.group(2), 16)
        mem_file.seek(start)  # seek to region start
        togo = end-start # number of bytes to read
        while togo > CHUNKSIZE: # read sequential memory from region one block at the moment
            mem_file.read(CHUNKSIZE) 
            togo -= CHUNKSIZE
            total += CHUNKSIZE
            print(total/1048576) # be verbose, print megabytes read so far
        mem_file.read(togo) # read remaining region contents
        total+=togo # dump contents to standard output
        print(total/1048576) # be verbose...
maps_file.close()
mem_file.close()

メモリの最後のバイトで失敗することもありますが、通常は同じパフォーマンスで動作しswapoff、指定されたプロセスのみをロードします。スクリプトは、この答え

関連情報